;**************************************************************
;*
;*             C P / M   VERSION   2 . 2
;*
;*   RECONSTRUCTED FROM MEMORY IMAGE ON FEBRUARY 27, 1981
;*
;*                BY CLARK A. CALKINS
;*
;**************************************************************
;
;   SET MEMORY LIMIT HERE. THIS IS THE AMOUNT OF CONTIGEOUS
; RAM STARTING FROM 0000. CP/M WILL RESIDE AT THE END OF THIS SPACE.
; all instructions changed to .z80 mnemonics with XIZ.COM
; change dbgmonitor so it boots into cp/m directly
; changed 3 files on b drive 
;
; add conditional assembly instructions here either .EQU true or .EQU false
; all conditional assembly groups start with CONDxxxx:
; default is true ie the original instructions eg IF TSR is the original code
; so to add a new group set the name to false else set it to true for the old code
FALSE:		.EQU 0
TRUE:		.EQU 1

; list of conditional assembly instructions

CONDSWAPAB:	.EQU	TRUE   ; true for original, false for new code=A=ram
CONDIDESOFT:	.EQU	TRUE	; if no ide drive, has a significant delay on soft boot (true) or quick (false)
CONDWBOOT:	.EQU	FALSE	; warm boot completely reloads cp/m (true) or doesn't (false)
CONDTSR		.EQU	TRUE	; true for original, false to add TSR routines
CONDSHORTMSG	.EQU	TRUE	; true for original warm boot signon, false for shorter one with less <CR>
CONDSUPERSUB	.EQU	TRUE	; true for no supersub autoexec, false to run supersub autoexec
CONDKEYBOARD	.EQU	TRUE	; true for original, false to scan PS2 keyboard in CONIN as well as serial
CONDCONOUT	.EQU	TRUE	; true for original, false to run echooff.com TSR to maintain radio silence and LCD
CONDUSRPATCH	.EQU	TRUE	; true for original, false to add A1> for user number as well as drive
CONDMASKCONIN 	.EQU    TRUE   ; true for original, false to add a >127 mask for conin
CONDABONLY    	.EQU    TRUE   ; true for original, false to only have drive A and B
CONDINVALIDDRV	.EQU    FALSE   ; true for original, false to go to A if an invalid drive selected

; 22nd March, 2009 - added AND 127 to the uart input (2 places) so ignores rubbish characters from an xmodem fail
; see conin

; ***************** START OF CP/M CODE ****************************

MEM:	.EQU 60			; FOR A 62K SYSTEM (TS802 TEST - WORKS OK).
;
IOBYTE:	.EQU 3			; I/O DEFINITION BYTE.
TDRIVE:	.EQU 4			; CURRENT DRIVE NAME AND USER NUMBER.
ENTRY:	.EQU 5			; ENTRY POINT FOR THE CP/M BDOS.
TFCB:	.EQU 5CH		; DEFAULT FILE CONTROL BLOCK.
TBUFF:	.EQU 80H		; I/O BUFFER AND COMMAND LINE STORAGE.
TBASE:	.EQU 100H		; TRANSIANT PROGRAM STORAGE AREA.
;
;   SET CONTROL CHARACTER .EQUATES.
;
CNTRLC:	.EQU 3			; CONTROL-C
CNTRLE:	.EQU 05H		; CONTROL-E
BS:	.EQU 08H		; BACKSPACE
TAB:	.EQU 09H		; TAB
LF:	.EQU 0AH		; LINE FEED
FF:	.EQU 0CH		; FORM FEED
CR:	.EQU 0DH		; CARRIAGE RETURN
CNTRLP:	.EQU 10H		; CONTROL-P
CNTRLR:	.EQU 12H		; CONTROL-R
CNTRLS:	.EQU 13H		; CONTROL-S
CNTRLU:	.EQU 15H		; CONTROL-U
CNTRLX:	.EQU 18H		; CONTROL-X
CNTRLZ:	.EQU 1AH		; CONTROL-Z (END-OF-FILE MARK)
DEL:	.EQU 7FH		; RUBOUT
;
;   SET ORIGIN FOR CP/M
;
	.ORG (MEM-7)*1024
;
CBASE:	JP	COMMAND		; EXECUTE COMMAND PROCESSOR (CCP).
	JP	CLEARBUF	; ENTRY TO EMPTY INPUT BUFFER BEFORE STARTING CCP.

;
;   STANDARD CP/M CCP INPUT BUFFER. FORMAT IS (MAX LENGTH),
; (ACTUAL LENGTH), (CHAR #1), (CHAR #2), (CHAR #3), ETC.
;
; conditional assembly for supersub

	.IF 	CONDSUPERSUB

INBUFF:	.DB 127			; LENGTH OF INPUT BUFFER.
	.DB 0			; CURRENT LENGTH OF CONTENTS.
	.DB "COPYRIGHT"
	.DB " 1979 (C) BY "
	.DB "DIGITAL RESEARCH      "
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

	.ELSE
; modified code

INBUFF:	.DB 127			; LENGTH OF INPUT BUFFER.
; N8VEM - if add any text after this point, change db 0 below to length
; and put a 0 after the text, and delete the same number of zeros after the dig
; so that inpoint ends up at the same spot
; INBUFF+1 is cleared on the next warm boot, so only runs once.

	.DB 17			; Autoboot length of string
	.DB    "SUPERSUB AUTOEXEC"
	.DB 0			; zero at end
	.DB "COPYRIGHT"
	.DB " 1979 (C) BY "
	.DB "DIGITAL RESEARCH      "
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; delete 6 zeros N8VEM
	.DB 0,0,0		; N8VEM now 3 zeros instead of 15 - comment out line above

	.ENDIF

; back to unconditional assembly

INPOINT: .DW INBUFF+2		; INPUT LINE POINTER
NAMEPNT: .DW 0			; INPUT LINE POINTER USED FOR ERROR MESSAGE. POINTS TO
;			;START OF NAME IN ERROR.
;
;   ROUTINE TO PRINT (A) ON THE CONSOLE. ALL REGISTERS USED.
;
PRINT:	LD	E,A		; SETUP BDOS CALL.
	LD	C,2
	JP	ENTRY
;
;   ROUTINE TO PRINT (A) ON THE CONSOLE AND TO SAVE (BC).
;
PRINTB:	PUSH	BC
	CALL	PRINT
	POP	BC
	RET
;
;   ROUTINE TO SEND A CARRIAGE RETURN, LINE FEED COMBINATION
; TO THE CONSOLE.
;
CRLF:	LD	A,CR
	CALL	PRINTB
	LD	A,LF
	JP	PRINTB
;
;   ROUTINE TO SEND ONE SPACE TO THE CONSOLE AND SAVE (BC).
;
SPACE:	LD	A,' '
	JP	PRINTB
;
;   ROUTINE TO PRINT CHARACTER STRING POINTED TO BE (BC) ON THE
; CONSOLE. IT MUST TERMINATE WITH A NULL BYTE.
;
PLINE:	PUSH	BC
	CALL	CRLF
	POP	HL
PLINE2:	LD	A,(HL)
	OR	A
	RET	Z
	INC	HL
	PUSH	HL
	CALL	PRINT
	POP	HL
	JP	PLINE2
;
;   ROUTINE TO RESET THE DISK SYSTEM.
;
RESDSK:	LD	C,13
	JP	ENTRY
;
;   ROUTINE TO SELECT DISK (A).
;
DSKSEL:	LD	E,A
	LD	C,14
	JP	ENTRY
;
;   ROUTINE TO CALL BDOS AND SAVE THE RETURN CODE. THE ZERO
; FLAG IS SET ON A RETURN OF 0FFH.
;
ENTRY1:	CALL	ENTRY
	LD	(RTNCODE),A	; SAVE RETURN CODE.
	INC	A		; SET ZERO IF 0FFH RETURNED.
	RET
;
;   ROUTINE TO OPEN A FILE. (DE) MUST POINT TO THE FCB.
;
OPEN:	LD	C,15
	JP	ENTRY1
;
;   ROUTINE TO OPEN FILE AT (FCB).
;
OPENFCB:XOR	A		; CLEAR THE RECORD NUMBER BYTE AT FCB+32
	LD	(FCB+32),A
	LD	DE,FCB
	JP	OPEN
;
;   ROUTINE TO CLOSE A FILE. (DE) POINTS TO FCB.
;
CLOSE:	LD	C,16
	JP	ENTRY1
;
;   ROUTINE TO SEARCH FOR THE FIRST FILE WITH AMBIGUEOUS NAME
; (DE).
;
SRCHFST:LD	C,17
	JP	ENTRY1
;
;   SEARCH FOR THE NEXT AMBIGEOUS FILE NAME.
;
SRCHNXT:LD	C,18
	JP	ENTRY1
;
;   SEARCH FOR FILE AT (FCB).
;
SRCHFCB:LD	DE,FCB
	JP	SRCHFST
;
;   ROUTINE TO DELETE A FILE POINTED TO BY (DE).
;
DELETE:	LD	C,19
	JP	ENTRY
;
;   ROUTINE TO CALL THE BDOS AND SET THE ZERO FLAG IF A ZERO
; STATUS IS RETURNED.
;
ENTRY2:	CALL	ENTRY
	OR	A		; SET ZERO FLAG IF APPROPRIATE.
	RET
;
;   ROUTINE TO READ THE NEXT RECORD FROM A S.EQUENTIAL FILE.
; (DE) POINTS TO THE FCB.
;
RDREC:	LD	C,20
	JP	ENTRY2
;
;   ROUTINE TO READ FILE AT (FCB).
;
READFCB:LD	DE,FCB
	JP	RDREC
;
;   ROUTINE TO WRITE THE NEXT RECORD OF A S.EQUENTIAL FILE.
; (DE) POINTS TO THE FCB.
;
WRTREC:	LD	C,21
	JP	ENTRY2
;
;   ROUTINE TO CREATE THE FILE POINTED TO BY (DE).
;
CREATE:	LD	C,22
	JP	ENTRY1
;
;   ROUTINE TO RENAME THE FILE POINTED TO BY (DE). NOTE THAT
; THE NEW NAME STARTS AT (DE+16).
;
RENAM:	LD	C,23
	JP	ENTRY
;
;   GET THE CURRENT USER CODE.
;
GETUSR:	LD	E,0FFH
;
;   ROUTNE TO GET OR SET THE CURRENT USER CODE.
; IF (E) IS FF THEN THIS IS A GET, ELSE IT IS A SET.
;
GETSETUC:
	LD	C,32
	JP	ENTRY
;
;   ROUTINE TO SET THE CURRENT DRIVE BYTE AT (TDRIVE).
;
SETCDRV:CALL	GETUSR		; GET USER NUMBER
	ADD	A,A		; AND SHIFT INTO THE UPPER 4 BITS.
	ADD	A,A
	ADD	A,A
	ADD	A,A
	LD	HL,CDRIVE	; NOW ADD IN THE CURRENT DRIVE NUMBER.
	OR	(HL)
	LD	(TDRIVE),A	; AND SAVE.
	RET
;
;   MOVE CURRENTLY ACTIVE DRIVE DOWN TO (TDRIVE).
;
MOVECD:	LD	A,(CDRIVE)
	LD	(TDRIVE),A
	RET
;
;   ROUTINE TO CONVERT (A) INTO UPPER CASE ASCII. ONLY LETTERS
; ARE AFFECTED.
;
UPPER:	CP	'A'		; CHECK FOR LETTERS IN THE RANGE OF 'A' TO 'Z'.
	RET	C
	CP	'{'
	RET	NC
	AND	5FH		; CONVERT IT IF FOUND.
	RET
;
;   ROUTINE TO GET A LINE OF INPUT. WE MUST CHECK TO SEE IF THE
; USER IS IN (BATCH) MODE. IF SO, THEN READ THE INPUT FROM FILE
; ($$$.SUB). AT THE END, RESET TO CONSOLE INPUT.
;
GETINP:	LD	A,(BATCH)	; IF =0, THEN USE CONSOLE INPUT.
	OR	A
	JP	Z,GETINP1
;
;   USE THE SUBMIT FILE ($$$.SUB) WHICH IS PREPARED BY A
; SUBMIT RUN. IT MUST BE ON DRIVE (A) AND IT WILL BE DELETED
; IF AND ERROR OCCURES (LIKE EOF).
;
	LD	A,(CDRIVE)	; SELECT DRIVE 0 IF NEED BE.
	OR	A
	LD	A,0		; ALWAYS USE DRIVE A FOR SUBMIT.
	CALL	NZ,DSKSEL	; SELECT IT IF REQUIRED.
	LD	DE,BATCHFCB
	CALL	OPEN		; LOOK FOR IT.
	JP	Z,GETINP1	; IF NOT THERE, USE NORMAL INPUT.
	LD	A,(BATCHFCB+15)	; GET LAST RECORD NUMBER+1.
	DEC	A
	LD	(BATCHFCB+32),A
	LD	DE,BATCHFCB
	CALL	RDREC		; READ LAST RECORD.
	JP	NZ,GETINP1	; QUIT ON END OF FILE.
;
;   MOVE THIS RECORD INTO INPUT BUFFER.
;
	LD	DE,INBUFF+1
	LD	HL,TBUFF	; DATA WAS READ INTO BUFFER HERE.
	LD	B,128		; ALL 128 CHARACTERS MAY BE USED.
	CALL	HL2DE		; (HL) TO (DE), (B) BYTES.
	LD	HL,BATCHFCB+14
	LD	(HL),0		; ZERO OUT THE 'S2' BYTE.
	INC	HL		; AND DECREMENT THE RECORD COUNT.
	DEC	(HL)
	LD	DE,BATCHFCB	; CLOSE THE BATCH FILE NOW.
	CALL	CLOSE
	JP	Z,GETINP1	; QUIT ON AN ERROR.
	LD	A,(CDRIVE)	; RE-SELECT PREVIOUS DRIVE IF NEED BE.
	OR	A
	CALL	NZ,DSKSEL	; DON'T DO NEEDLESS SELECTS.
;
;   PRINT LINE JUST READ ON CONSOLE.
;
	LD	HL,INBUFF+2
	CALL	PLINE2
	CALL	CHKCON		; CHECK CONSOLE, QUIT ON A KEY.
	JP	Z,GETINP2	; JUMP IF NO KEY IS PRESSED.
;
;   TERMINATE THE SUBMIT JOB ON ANY KEYBOARD INPUT. DELETE THIS
; FILE SUCH THAT IT IS NOT RE-STARTED AND JUMP TO NORMAL KEYBOARD
; INPUT SECTION.
;
	CALL	DELBATCH	; DELETE THE BATCH FILE.
	JP	CMMND1		; AND RESTART COMMAND INPUT.
;
;   GET HERE FOR NORMAL KEYBOARD INPUT. DELETE THE SUBMIT FILE
; INCASE THERE WAS ONE.
;
GETINP1:CALL	DELBATCH	; DELETE FILE ($$$.SUB).
	CALL	SETCDRV		; RESET ACTIVE DISK.
	LD	C,10		; GET LINE FROM CONSOLE DEVICE.
	LD	DE,INBUFF
	CALL	ENTRY
	CALL	MOVECD		; RESET CURRENT DRIVE (AGAIN).
;
;   CONVERT INPUT LINE TO UPPER CASE.
;
GETINP2:LD	HL,INBUFF+1
	LD	B,(HL)		; (B)=CHARACTER COUNTER.
GETINP3:INC	HL
	LD	A,B		; END OF THE LINE?
	OR	A
	JP	Z,GETINP4
	LD	A,(HL)		; CONVERT TO UPPER CASE.
	CALL	UPPER
	LD	(HL),A
	DEC	B		; ADJUST CHARACTER COUNT.
	JP	GETINP3
GETINP4:LD	(HL),A		; ADD TRAILING NULL.
	LD	HL,INBUFF+2
	LD	(INPOINT),HL	; RESET INPUT LINE POINTER.
	RET
;
;   ROUTINE TO CHECK THE CONSOLE FOR A KEY PRESSED. THE ZERO
; FLAG IS SET IS NONE, ELSE THE CHARACTER IS RETURNED IN (A).
;
CHKCON:	LD	C,11		; CHECK CONSOLE.
	CALL	ENTRY
	OR	A
	RET	Z		; RETURN IF NOTHING.
	LD	C,1		; ELSE GET CHARACTER.
	CALL	ENTRY
	OR	A		; CLEAR ZERO FLAG AND RETURN.
	RET
;
;   ROUTINE TO GET THE CURRENTLY ACTIVE DRIVE NUMBER.
;
GETDSK:	LD	C,25
	JP	ENTRY
;
;   SET THE STABDARD DMA ADDRESS.
;
STDDMA:	LD	DE,TBUFF
;
;   ROUTINE TO SET THE DMA ADDRESS TO (DE).
;
DMASET:	LD	C,26
	JP	ENTRY
;
;  DELETE THE BATCH FILE CREATED BY SUBMIT.
;
DELBATCH:
	LD	HL,BATCH	; IS BATCH ACTIVE?
	LD	A,(HL)
	OR	A
	RET	Z
	LD	(HL),0		; YES, DE-ACTIVATE IT.
	XOR	A
	CALL	DSKSEL		; SELECT DRIVE 0 FOR SURE.
	LD	DE,BATCHFCB	; AND DELETE THIS FILE.
	CALL	DELETE
	LD	A,(CDRIVE)	; RESET CURRENT DRIVE.
	JP	DSKSEL
;
;   CHECK TO TWO STRINGS AT (PATTRN1) AND (PATTRN2). THEY MUST BE
; THE SAME OR WE HALT....
;
VERIFY:	LD	DE,PATTRN1	; THESE ARE THE SERIAL NUMBER BYTES.
	LD	HL,PATTRN2	; DITTO, BUT HOW COULD THEY BE DIFFERENT?
	LD	B,6		; 6 BYTES EACH.
VERIFY1:LD	A,(DE)
	CP	(HL)
	JP	NZ,HALT		; JUMP TO HALT ROUTINE.
	INC	DE
	INC	HL
	DEC	B
	JP	NZ,VERIFY1
	RET
;
;   PRINT BACK FILE NAME WITH A '?' TO INDICATE A SYNTAX ERROR.
;
SYNERR:	CALL	CRLF		; END CURRENT LINE.
	LD	HL,(NAMEPNT)	; THIS POINTS TO NAME IN ERROR.
SYNERR1:LD	A,(HL)		; PRINT IT UNTIL A SPACE OR NULL IS FOUND.
	CP	' '
	JP	Z,SYNERR2
	OR	A
	JP	Z,SYNERR2
	PUSH	HL
	CALL	PRINT
	POP	HL
	INC	HL
	JP	SYNERR1
SYNERR2:LD	A,'?'		; ADD TRAILING '?'.
	CALL	PRINT
	CALL	CRLF
	CALL	DELBATCH	; DELETE ANY BATCH FILE.
	JP	CMMND1		; AND RESTART FROM CONSOLE INPUT.
;
;   CHECK CHARACTER AT (DE) FOR LEGAL COMMAND INPUT. NOTE THAT THE
; ZERO FLAG IS SET IF THE CHARACTER IS A DELIMITER.
;
CHECK:	LD	A,(DE)
	OR	A
	RET	Z
	CP	' '		; CONTROL CHARACTERS ARE NOT LEGAL HERE.
	JP	C,SYNERR
	RET	Z		; CHECK FOR VALID DELIMITER.
	CP	'='
	RET	Z
	CP	'_'
	RET	Z
	CP	'.'
	RET	Z
	CP	':'
	RET	Z
	CP	03BH		; SEMICOLON ';'
	RET	Z
	CP	'<'
	RET	Z
	CP	'>'
	RET	Z
	RET
;
;   GET THE NEXT NON-BLANK CHARACTER FROM (DE).
;
NONBLANK:
	LD	A,(DE)
	OR	A		; STRING ENDS WITH A NULL.
	RET	Z
	CP	' '
	RET	NZ
	INC	DE
	JP	NONBLANK
;
;   ADD (HL)=(HL)+(A)
;
ADDHL:	ADD	A,L
	LD	L,A
	RET	NC		; TAKE CARE OF ANY CARRY.
	INC	H
	RET
;
;   CONVERT THE FIRST NAME IN (FCB).
;
CONVFST:LD	A,0
;
;   FORMAT A FILE NAME (CONVERT * TO '?', ETC.). ON RETURN,
; (A)=0 IS AN UNAMBIGEOUS NAME WAS SPECIFIED. ENTER WITH (A) .EQUAL TO
; THE POSITION WITHIN THE FCB FOR THE NAME (EITHER 0 OR 16).
;
CONVERT:LD	HL,FCB
	CALL	ADDHL
	PUSH	HL
	PUSH	HL
	XOR	A
	LD	(CHGDRV),A	; INITIALIZE DRIVE CHANGE FLAG.
	LD	HL,(INPOINT)	; SET (HL) AS POINTER INTO INPUT LINE.
	EX	DE,HL
	CALL	NONBLANK	; GET NEXT NON-BLANK CHARACTER.
	EX	DE,HL
	LD	(NAMEPNT),HL	; SAVE POINTER HERE FOR ANY ERROR MESSAGE.
	EX	DE,HL
	POP	HL
	LD	A,(DE)		; GET FIRST CHARACTER.
	OR	A
	JP	Z,CONVRT1
	SBC	A,'A'-1		; MIGHT BE A DRIVE NAME, CONVERT TO BINARY.
	LD	B,A		; AND SAVE.
	INC	DE		; CHECK NEXT CHARACTER FOR A ':'.
	LD	A,(DE)
	CP	':'
	JP	Z,CONVRT2
	DEC	DE		; NOPE, MOVE POINTER BACK TO THE START OF THE LINE.
CONVRT1:LD	A,(CDRIVE)
	LD	(HL),A
	JP	CONVRT3
CONVRT2:LD	A,B
	LD	(CHGDRV),A	; SET CHANGE IN DRIVES FLAG.
	LD	(HL),B
	INC	DE
;
;   CONVERT THE BASIC FILE NAME.
;
CONVRT3:LD	B,08H
CONVRT4:CALL	CHECK
	JP	Z,CONVRT8
	INC	HL
	CP	'*'		; NOTE THAT AN '*' WILL FILL THE REMAINING
	JP	NZ,CONVRT5	; FIELD WITH '?'.
	LD	(HL),'?'
	JP	CONVRT6
CONVRT5:LD	(HL),A
	INC	DE
CONVRT6:DEC	B
	JP	NZ,CONVRT4
CONVRT7:CALL	CHECK		; GET NEXT DELIMITER.
	JP	Z,GETEXT
	INC	DE
	JP	CONVRT7
CONVRT8:INC	HL		; BLANK FILL THE FILE NAME.
	LD	(HL),' '
	DEC	B
	JP	NZ,CONVRT8
;
;   GET THE EXTENSION AND CONVERT IT.
;
GETEXT:	LD	B,03H
	CP	'.'
	JP	NZ,GETEXT5
	INC	DE
GETEXT1:CALL	CHECK
	JP	Z,GETEXT5
	INC	HL
	CP	'*'
	JP	NZ,GETEXT2
	LD	(HL),'?'
	JP	GETEXT3
GETEXT2:LD	(HL),A
	INC	DE
GETEXT3:DEC	B
	JP	NZ,GETEXT1
GETEXT4:CALL	CHECK
	JP	Z,GETEXT6
	INC	DE
	JP	GETEXT4
GETEXT5:INC	HL
	LD	(HL),' '
	DEC	B
	JP	NZ,GETEXT5
GETEXT6:LD	B,3
GETEXT7:INC	HL
	LD	(HL),0
	DEC	B
	JP	NZ,GETEXT7
	EX	DE,HL
	LD	(INPOINT),HL	; SAVE INPUT LINE POINTER.
	POP	HL
;
;   CHECK TO SEE IF THIS IS AN AMBIGEOUS FILE NAME SPECIFICATION.
; SET THE (A) REGISTER TO NON ZERO IF IT IS.
;
	LD	BC,11		; SET NAME LENGTH.
GETEXT8:INC	HL
	LD	A,(HL)
	CP	'?'		; ANY QUESTION MARKS?
	JP	NZ,GETEXT9
	INC	B		; COUNT THEM.
GETEXT9:DEC	C
	JP	NZ,GETEXT8
	LD	A,B
	OR	A
	RET
;
;   CP/M COMMAND TABLE. NOTE COMMANDS CAN BE EITHER 3 OR 4 CHARACTERS LONG.
;
NUMCMDS:.EQU 6			; NUMBER OF COMMANDS
CMDTBL:	.DB "DIR "
	.DB "ERA "
	.DB "TYPE"
	.DB "SAVE"
	.DB "REN "
	.DB "USER"
;
;   THE FOLLOWING SIX BYTES MUST AGREE WITH THOSE AT (PATTRN2)
; OR CP/M WILL HALT. WHY?
;
PATTRN1:.DB 0,22,0,0,0,0	; (* SERIAL NUMBER BYTES *).
;
;   SEARCH THE COMMAND TABLE FOR A MATCH WITH WHAT HAS JUST
; BEEN ENTERED. IF A MATCH IS FOUND, THEN WE JUMP TO THE
; PROPER SECTION. ELSE JUMP TO (UNKNOWN).
; ON RETURN, THE (C) REGISTER IS SET TO THE COMMAND NUMBER
; THAT MATCHED (OR NUMCMDS+1 IF NO MATCH).
;
SEARCH:	LD	HL,CMDTBL
	LD	C,0
SEARCH1:LD	A,C
	CP	NUMCMDS		; THIS COMMANDS EXISTS.
	RET	NC
	LD	DE,FCB+1	; CHECK THIS ONE.
	LD	B,4		; MAX COMMAND LENGTH.
SEARCH2:LD	A,(DE)
	CP	(HL)
	JP	NZ,SEARCH3	; NOT A MATCH.
	INC	DE
	INC	HL
	DEC	B
	JP	NZ,SEARCH2
	LD	A,(DE)		; ALLOW A 3 CHARACTER COMMAND TO MATCH.
	CP	' '
	JP	NZ,SEARCH4
	LD	A,C		; SET RETURN REGISTER FOR THIS COMMAND.
	RET
SEARCH3:INC	HL
	DEC	B
	JP	NZ,SEARCH3
SEARCH4:INC	C
	JP	SEARCH1
;
;   SET THE INPUT BUFFER TO EMPTY AND THEN START THE COMMAND
; PROCESSOR (CCP).
;
CLEARBUF:
	XOR	A
	LD	(INBUFF+1),A	; SECOND BYTE IS ACTUAL LENGTH.
;
;**************************************************************
;*
;*
;* C C P  -   C O N S O L E   C O M M A N D   P R O C E S S O R
;*
;**************************************************************
;*
COMMAND:LD	SP,CCPSTACK	; SETUP STACK AREA.
	PUSH	BC		; NOTE THAT (C) SHOULD BE .EQUAL TO:
	LD	A,C		; (UUUUDDDD) WHERE 'UUUU' IS THE USER NUMBER
	RRA			; AND 'DDDD' IS THE DRIVE NUMBER.
	RRA
	RRA
	RRA
	AND	0FH		; ISOLATE THE USER NUMBER.
	LD	E,A
	CALL	GETSETUC	; AND SET IT.
	CALL	RESDSK		; RESET THE DISK SYSTEM.
	LD	(BATCH),A	; CLEAR BATCH MODE FLAG.
	POP	BC
	LD	A,C
	AND	0FH		; ISOLATE THE DRIVE NUMBER.
	LD	(CDRIVE),A	; AND SAVE.
	CALL	DSKSEL		; ...AND SELECT.
	LD	A,(INBUFF+1)
	OR	A		; ANYTHING IN INPUT BUFFER ALREADY?
	JP	NZ,CMMND2	; YES, WE JUST PROCESS IT.
;
;   ENTRY POINT TO GET A COMMAND LINE FROM THE CONSOLE.
;
CMMND1:	LD	SP,CCPSTACK	; SET STACK STRAIGHT.
	CALL	CRLF		; START A NEW LINE ON THE SCREEN.
	CALL	GETDSK		; GET CURRENT DRIVE.
	ADD	A,'A'
	CALL	PRINT		; PRINT CURRENT DRIVE.
	.IF 	CONDUSRPATCH
				; original code does not call this
	.ELSE	
	CALL	USRPAT		; call patch to print USER number
	.ENDIF
	LD	A,'>'
	CALL	PRINT		; AND ADD PROMPT.
	CALL	GETINP		; GET LINE FROM USER.
;
;   PROCESS COMMAND LINE HERE.
;
CMMND2:	LD	DE,TBUFF
	CALL	DMASET		; SET STANDARD DMA ADDRESS.
	CALL	GETDSK
	LD	(CDRIVE),A	; SET CURRENT DRIVE.
	CALL	CONVFST		; CONVERT NAME TYPED IN.
	CALL	NZ,SYNERR	; WILD CARDS ARE NOT ALLOWED.
	LD	A,(CHGDRV)	; IF A CHANGE IN DRIVES WAS INDICATED,
	OR	A		; THEN TREAT THIS AS AN UNKNOWN COMMAND
	JP	NZ,UNKNOWN	; WHICH GETS EXECUTED.
	CALL	SEARCH		; ELSE SEARCH COMMAND TABLE FOR A MATCH.
;
;   NOTE THAT AN UNKNOWN COMMAND RETURNS
; WITH (A) POINTING TO THE LAST ADDRESS
; IN OUR TABLE WHICH IS (UNKNOWN).
;
	LD	HL,CMDADR	; NOW, LOOK THRU OUR ADDRESS TABLE FOR COMMAND (A).
	LD	E,A		; SET (DE) TO COMMAND NUMBER.
	LD	D,0
	ADD	HL,DE
	ADD	HL,DE		; (HL)=(CMDADR)+2*(COMMAND NUMBER).
	LD	A,(HL)		; NOW PICK OUT THIS ADDRESS.
	INC	HL
	LD	H,(HL)
	LD	L,A
	JP	(HL)		; NOW EXECUTE IT.
;
;   CP/M COMMAND ADDRESS TABLE.
;
CMDADR:	.DW DIRECT,ERASE,TYPE,SAVE
	.DW RENAME,USER,UNKNOWN
;
;   HALT THE SYSTEM. REASON FOR THIS IS UNKNOWN AT PRESENT.
;
HALT:	LD	HL,76F3H	; 'DI HLT' INSTRUCTIONS.
	LD	(CBASE),HL
	LD	HL,CBASE
	JP	(HL)
;
;   READ ERROR WHILE TYPEING A FILE.
;
RDERROR:LD	BC,RDERR
	JP	PLINE
RDERR:	.DB "READ ERROR",0
;
;   REQUIRED FILE WAS NOT LOCATED.
;
NONE:	LD	BC,NOFILE
	JP	PLINE
NOFILE:	.DB "NO FILE",0
;
;   DECODE A COMMAND OF THE FORM 'A>FILENAME NUMBER{ FILENAME}.
; NOTE THAT A DRIVE SPECIFIER IS NOT ALLOWED ON THE FIRST FILE
; NAME. ON RETURN, THE NUMBER IS IN REGISTER (A). ANY ERROR
; CAUSES 'FILENAME?' TO BE PRINTED AND THE COMMAND IS ABORTED.
;
DECODE:	CALL	CONVFST		; CONVERT FILENAME.
	LD	A,(CHGDRV)	; DO NOT ALLOW A DRIVE TO BE SPECIFIED.
	OR	A
	JP	NZ,SYNERR
	LD	HL,FCB+1	; CONVERT NUMBER NOW.
	LD	BC,11		; (B)=SUM REGISTER, (C)=MAX DIGIT COUNT.
DECODE1:LD	A,(HL)
	CP	' '		; A SPACE TERMINATES THE NUMERAL.
	JP	Z,DECODE3
	INC	HL
	SUB	'0'		; MAKE BINARY FROM ASCII.
	CP	10		; LEGAL DIGIT?
	JP	NC,SYNERR
	LD	D,A		; YES, SAVE IT IN (D).
	LD	A,B		; COMPUTE (B)=(B)*10 AND CHECK FOR OVERFLOW.
	AND	0E0H
	JP	NZ,SYNERR
	LD	A,B
	RLCA
	RLCA
	RLCA			; (A)=(B)*8
	ADD	A,B		; .......*9
	JP	C,SYNERR
	ADD	A,B		; .......*10
	JP	C,SYNERR
	ADD	A,D		; ADD IN NEW DIGIT NOW.
DECODE2:JP	C,SYNERR
	LD	B,A		; AND SAVE RESULT.
	DEC	C		; ONLY LOOK AT 11 DIGITS.
	JP	NZ,DECODE1
	RET
DECODE3:LD	A,(HL)		; SPACES MUST FOLLOW (WHY?).
	CP	' '
	JP	NZ,SYNERR
	INC	HL
DECODE4:DEC	C
	JP	NZ,DECODE3
	LD	A,B		; SET (A)=THE NUMERIC VALUE ENTERED.
	RET
;
;   MOVE 3 BYTES FROM (HL) TO (DE). NOTE THAT THERE IS ONLY
; ONE REFERENCE TO THIS AT (A2D5H).
;
MOVE3:	LD	B,3
;
;   MOVE (B) BYTES FROM (HL) TO (DE).
;
HL2DE:	LD	A,(HL)
	LD	(DE),A
	INC	HL
	INC	DE
	DEC	B
	JP	NZ,HL2DE
	RET
;
;   COMPUTE (HL)=(TBUFF)+(A)+(C) AND GET THE BYTE THAT'S HERE.
;
EXTRACT:LD	HL,TBUFF
	ADD	A,C
	CALL	ADDHL
	LD	A,(HL)
	RET
;
;  CHECK DRIVE SPECIFIED. IF IT MEANS A CHANGE, THEN THE NEW
; DRIVE WILL BE SELECTED. IN ANY CASE, THE DRIVE BYTE OF THE
; FCB WILL BE SET TO NULL (MEANS USE CURRENT DRIVE).
;
DSELECT:XOR	A		; NULL OUT FIRST BYTE OF FCB.
	LD	(FCB),A
	LD	A,(CHGDRV)	; A DRIVE CHANGE INDICATED?
	OR	A
	RET	Z
	DEC	A		; YES, IS IT THE SAME AS THE CURRENT DRIVE?
	LD	HL,CDRIVE
	CP	(HL)
	RET	Z
	JP	DSKSEL		; NO. SELECT IT THEN.
;
;   CHECK THE DRIVE SELECTION AND RESET IT TO THE PREVIOUS
; DRIVE IF IT WAS CHANGED FOR THE PRECEEDING COMMAND.
;
RESETDR:LD	A,(CHGDRV)	; DRIVE CHANGE INDICATED?
	OR	A
	RET	Z
	DEC	A		; YES, WAS IT A DIFFERENT DRIVE?
	LD	HL,CDRIVE
	CP	(HL)
	RET	Z
	LD	A,(CDRIVE)	; YES, RE-SELECT OUR OLD DRIVE.
	JP	DSKSEL
;
;**************************************************************
;*
;*           D I R E C T O R Y   C O M M A N D
;*
;**************************************************************
;
DIRECT:	CALL	CONVFST		; CONVERT FILE NAME.
	CALL	DSELECT		; SELECT INDICATED DRIVE.
	LD	HL,FCB+1	; WAS ANY FILE INDICATED?
	LD	A,(HL)
	CP	' '
	JP	NZ,DIRECT2
	LD	B,11		; NO. FILL FIELD WITH '?' - SAME AS *.*.
DIRECT1:LD	(HL),'?'
	INC	HL
	DEC	B
	JP	NZ,DIRECT1
DIRECT2:LD	E,0		; SET INITIAL CURSOR POSITION.
	PUSH	DE
	CALL	SRCHFCB		; GET FIRST FILE NAME.
	CALL	Z,NONE		; NONE FOUND AT ALL?
DIRECT3:JP	Z,DIRECT9	; TERMINATE IF NO MORE NAMES.
	LD	A,(RTNCODE)	; GET FILE'S POSITION IN SEGMENT (0-3).
	RRCA
	RRCA
	RRCA
	AND	60H		; (A)=POSITION*32
	LD	C,A
	LD	A,10
	CALL	EXTRACT		; EXTRACT THE TENTH ENTRY IN FCB.
	RLA			; CHECK SYSTEM FILE STATUS BIT.
	JP	C,DIRECT8	; WE DON'T LIST THEM.
	POP	DE
	LD	A,E		; BUMP NAME COUNT.
	INC	E
	PUSH	DE
	AND	03H		; AT END OF LINE?
	PUSH	AF
	JP	NZ,DIRECT4
	CALL	CRLF		; YES, END THIS LINE AND START ANOTHER.
	PUSH	BC
	CALL	GETDSK		; START LINE WITH ('A:').
	POP	BC
	ADD	A,'A'
	CALL	PRINTB
	LD	A,':'
	CALL	PRINTB
	JP	DIRECT5
DIRECT4:CALL	SPACE		; ADD SEPERATOR BETWEEN FILE NAMES.
	LD	A,':'
	CALL	PRINTB
DIRECT5:CALL	SPACE
	LD	B,1		; 'EXTRACT' EACH FILE NAME CHARACTER AT A TIME.
DIRECT6:LD	A,B
	CALL	EXTRACT
	AND	7FH		; STRIP BIT 7 (STATUS BIT).
	CP	' '		; ARE WE AT THE END OF THE NAME?
	JP	NZ,DRECT65
	POP	AF		; YES, DON'T PRINT SPACES AT THE END OF A LINE.
	PUSH	AF
	CP	3
	JP	NZ,DRECT63
	LD	A,9		; FIRST CHECK FOR NO EXTENSION.
	CALL	EXTRACT
	AND	7FH
	CP	' '
	JP	Z,DIRECT7	; DON'T PRINT SPACES.
DRECT63:LD	A,' '		; ELSE PRINT THEM.
DRECT65:CALL	PRINTB
	INC	B		; BUMP TO NEXT CHARACTER PSOITION.
	LD	A,B
	CP	12		; END OF THE NAME?
	JP	NC,DIRECT7
	CP	9		; NOPE, STARTING EXTENSION?
	JP	NZ,DIRECT6
	CALL	SPACE		; YES, ADD SEPERATING SPACE.
	JP	DIRECT6
DIRECT7:POP	AF		; GET THE NEXT FILE NAME.
DIRECT8:CALL	CHKCON		; FIRST CHECK CONSOLE, QUIT ON ANYTHING.
	JP	NZ,DIRECT9
	CALL	SRCHNXT		; GET NEXT NAME.
	JP	DIRECT3		; AND CONTINUE WITH OUR LIST.
DIRECT9:POP	DE		; RESTORE THE STACK AND RETURN TO COMMAND LEVEL.
	JP	GETBACK
;
;**************************************************************
;*
;*                E R A S E   C O M M A N D
;*
;**************************************************************
;
ERASE:	CALL	CONVFST		; CONVERT FILE NAME.
	CP	11		; WAS '*.*' ENTERED?
	JP	NZ,ERASE1
	LD	BC,YESNO	; YES, ASK FOR CONFIRMATION.
	CALL	PLINE
	CALL	GETINP
	LD	HL,INBUFF+1
	DEC	(HL)		; MUST BE EXACTLY 'Y'.
	JP	NZ,CMMND1
	INC	HL
	LD	A,(HL)
	CP	'Y'
	JP	NZ,CMMND1
	INC	HL
	LD	(INPOINT),HL	; SAVE INPUT LINE POINTER.
ERASE1:	CALL	DSELECT		; SELECT DESIRED DISK.
	LD	DE,FCB
	CALL	DELETE		; DELETE THE FILE.
	INC	A
	CALL	Z,NONE		; NOT THERE?
	JP	GETBACK		; RETURN TO COMMAND LEVEL NOW.
YESNO:	.DB "ALL (Y/N)?",0
;
;**************************************************************
;*
;*            T Y P E   C O M M A N D
;*
;**************************************************************
;
TYPE:	CALL	CONVFST		; CONVERT FILE NAME.
	JP	NZ,SYNERR	; WILD CARDS NOT ALLOWED.
	CALL	DSELECT		; SELECT INDICATED DRIVE.
	CALL	OPENFCB		; OPEN THE FILE.
	JP	Z,TYPE5		; NOT THERE?
	CALL	CRLF		; OK, START A NEW LINE ON THE SCREEN.
	LD	HL,NBYTES	; INITIALIZE BYTE COUNTER.
	LD	(HL),0FFH	; SET TO READ FIRST SECTOR.
TYPE1:	LD	HL,NBYTES
TYPE2:	LD	A,(HL)		; HAVE WE WRITTEN THE ENTIRE SECTOR?
	CP	128
	JP	C,TYPE3
	PUSH	HL		; YES, READ IN THE NEXT ONE.
	CALL	READFCB
	POP	HL
	JP	NZ,TYPE4	; END OR ERROR?
	XOR	A		; OK, CLEAR BYTE COUNTER.
	LD	(HL),A
TYPE3:	INC	(HL)		; COUNT THIS BYTE.
	LD	HL,TBUFF	; AND GET THE (A)TH ONE FROM THE BUFFER (TBUFF).
	CALL	ADDHL
	LD	A,(HL)
	CP	CNTRLZ		; END OF FILE MARK?
	JP	Z,GETBACK
	CALL	PRINT		; NO, PRINT IT.
	CALL	CHKCON		; CHECK CONSOLE, QUIT IF ANYTHING READY.
	JP	NZ,GETBACK
	JP	TYPE1
;
;   GET HERE ON AN END OF FILE OR READ ERROR.
;
TYPE4:	DEC	A		; READ ERROR?
	JP	Z,GETBACK
	CALL	RDERROR		; YES, PRINT MESSAGE.
TYPE5:	CALL	RESETDR		; AND RESET PROPER DRIVE
	JP	SYNERR		; NOW PRINT FILE NAME WITH PROBLEM.
;
;**************************************************************
;*
;*            S A V E   C O M M A N D
;*
;**************************************************************
;
SAVE:	CALL	DECODE		; GET NUMERIC NUMBER THAT FOLLOWS SAVE.
	PUSH	AF		; SAVE NUMBER OF PAGES TO WRITE.
	CALL	CONVFST		; CONVERT FILE NAME.
	JP	NZ,SYNERR	; WILD CARDS NOT ALLOWED.
	CALL	DSELECT		; SELECT SPECIFIED DRIVE.
	LD	DE,FCB		; NOW DELETE THIS FILE.
	PUSH	DE
	CALL	DELETE
	POP	DE
	CALL	CREATE		; AND CREATE IT AGAIN.
	JP	Z,SAVE3		; CAN'T CREATE?
	XOR	A		; CLEAR RECORD NUMBER BYTE.
	LD	(FCB+32),A
	POP	AF		; CONVERT PAGES TO SECTORS.
	LD	L,A
	LD	H,0
	ADD	HL,HL		; (HL)=NUMBER OF SECTORS TO WRITE.
	LD	DE,TBASE	; AND WE START FROM HERE.
SAVE1:	LD	A,H		; DONE YET?
	OR	L
	JP	Z,SAVE2
	DEC	HL		; NOPE, COUNT THIS AND COMPUTE THE START
	PUSH	HL		; OF THE NEXT 128 BYTE SECTOR.
	LD	HL,128
	ADD	HL,DE
	PUSH	HL		; SAVE IT AND SET THE TRANSFER ADDRESS.
	CALL	DMASET
	LD	DE,FCB		; WRITE OUT THIS SECTOR NOW.
	CALL	WRTREC
	POP	DE		; RESET (DE) TO THE START OF THE LAST SECTOR.
	POP	HL		; RESTORE SECTOR COUNT.
	JP	NZ,SAVE3	; WRITE ERROR?
	JP	SAVE1
;
;   GET HERE AFTER WRITING ALL OF THE FILE.
;
SAVE2:	LD	DE,FCB		; NOW CLOSE THE FILE.
	CALL	CLOSE
	INC	A		; DID IT CLOSE OK?
	JP	NZ,SAVE4
;
;   PRINT OUT ERROR MESSAGE (NO SPACE).
;
SAVE3:	LD	BC,NOSPACE
	CALL	PLINE
SAVE4:	CALL	STDDMA		; RESET THE STANDARD DMA ADDRESS.
	JP	GETBACK
NOSPACE:.DB "NO SPACE",0
;
;**************************************************************
;*
;*           R E N A M E   C O M M A N D
;*
;**************************************************************
;
RENAME:	CALL	CONVFST		; CONVERT FIRST FILE NAME.
	JP	NZ,SYNERR	; WILD CARDS NOT ALLOWED.
	LD	A,(CHGDRV)	; REMEMBER ANY CHANGE IN DRIVES SPECIFIED.
	PUSH	AF
	CALL	DSELECT		; AND SELECT THIS DRIVE.
	CALL	SRCHFCB		; IS THIS FILE PRESENT?
	JP	NZ,RENAME6	; YES, PRINT ERROR MESSAGE.
	LD	HL,FCB		; YES, MOVE THIS NAME INTO SECOND SLOT.
	LD	DE,FCB+16
	LD	B,16
	CALL	HL2DE
	LD	HL,(INPOINT)	; GET INPUT POINTER.
	EX	DE,HL
	CALL	NONBLANK	; GET NEXT NON BLANK CHARACTER.
	CP	'='		; ONLY ALLOW AN '=' OR '_' SEPERATOR.
	JP	Z,RENAME1
	CP	'_'
	JP	NZ,RENAME5
RENAME1:EX	DE,HL
	INC	HL		; OK, SKIP SEPERATOR.
	LD	(INPOINT),HL	; SAVE INPUT LINE POINTER.
	CALL	CONVFST		; CONVERT THIS SECOND FILE NAME NOW.
	JP	NZ,RENAME5	; AGAIN, NO WILD CARDS.
	POP	AF		; IF A DRIVE WAS SPECIFIED, THEN IT
	LD	B,A		; MUST BE THE SAME AS BEFORE.
	LD	HL,CHGDRV
	LD	A,(HL)
	OR	A
	JP	Z,RENAME2
	CP	B
	LD	(HL),B
	JP	NZ,RENAME5	; THEY WERE DIFFERENT, ERROR.
RENAME2:LD	(HL),B		; 	RESET AS PER THE FIRST FILE SPECIFICATION.
	XOR	A
	LD	(FCB),A		; CLEAR THE DRIVE BYTE OF THE FCB.
RENAME3:CALL	SRCHFCB		; AND GO LOOK FOR SECOND FILE.
	JP	Z,RENAME4	; DOESN'T EXIST?
	LD	DE,FCB
	CALL	RENAM		; OK, RENAME THE FILE.
	JP	GETBACK
;
;   PROCESS RENAME ERRORS HERE.
;
RENAME4:CALL	NONE		; FILE NOT THERE.
	JP	GETBACK
RENAME5:CALL	RESETDR		; BAD COMMAND FORMAT.
	JP	SYNERR
RENAME6:LD	BC,EXISTS	; DESTINATION FILE ALREADY EXISTS.
	CALL	PLINE
	JP	GETBACK
EXISTS:	.DB "FILE EXISTS",0
;
;**************************************************************
;*
;*             U S E R   C O M M A N D
;*
;**************************************************************
;
USER:	CALL	DECODE		; GET NUMERIC VALUE FOLLOWING COMMAND.
	CP	16		; LEGAL USER NUMBER?
	JP	NC,SYNERR
	LD	E,A		; YES BUT IS THERE ANYTHING ELSE?
	LD	A,(FCB+1)
	CP	' '
	JP	Z,SYNERR	; YES, THAT IS NOT ALLOWED.
	CALL	GETSETUC	; OK, SET USER CODE.
	JP	GETBACK1
;
;**************************************************************
;*
;*        T R A N S I A N T   P R O G R A M   C O M M A N D
;*
;**************************************************************
;
UNKNOWN:CALL	VERIFY		; CHECK FOR VALID SYSTEM (WHY?).
	LD	A,(FCB+1)	; ANYTHING TO EXECUTE?
	CP	' '
	JP	NZ,UNKWN1
	LD	A,(CHGDRV)	; NOPE, ONLY A DRIVE CHANGE?
	OR	A
	JP	Z,GETBACK1	; NEITHER???
	DEC	A
	LD	(CDRIVE),A	; OK, STORE NEW DRIVE.
	CALL	MOVECD		; SET (TDRIVE) ALSO.
	CALL	DSKSEL		; AND SELECT THIS DRIVE.
	JP	GETBACK1	; THEN RETURN.
;
;   HERE A FILE NAME WAS TYPED. PREPARE TO EXECUTE IT.
;
UNKWN1:	LD	DE,FCB+9	; AN EXTENSION SPECIFIED?
	LD	A,(DE)
	CP	' '
	JP	NZ,SYNERR	; YES, NOT ALLOWED.
UNKWN2:	PUSH	DE
	CALL	DSELECT		; SELECT SPECIFIED DRIVE.
	POP	DE
	LD	HL,COMFILE	; SET THE EXTENSION TO 'COM'.
	CALL	MOVE3
	CALL	OPENFCB		; AND OPEN THIS FILE.
	JP	Z,UNKWN9	; NOT PRESENT?
;
;   LOAD IN THE PROGRAM.
;
	LD	HL,TBASE	; STORE THE PROGRAM STARTING HERE.
UNKWN3:	PUSH	HL
	EX	DE,HL
	CALL	DMASET		; SET TRANSFER ADDRESS.
	LD	DE,FCB		; AND READ THE NEXT RECORD.
	CALL	RDREC
	JP	NZ,UNKWN4	; END OF FILE OR READ ERROR?
	POP	HL		; NOPE, BUMP POINTER FOR NEXT SECTOR.
	LD	DE,128
	ADD	HL,DE
	LD	DE,CBASE	; ENOUGH ROOM FOR THE WHOLE FILE?
	LD	A,L
	SUB	E
	LD	A,H
	SBC	A,D
	JP	NC,UNKWN0	; NO, IT CAN'T FIT.
	JP	UNKWN3
;
;   GET HERE AFTER FINISHED READING.
;
UNKWN4:	POP	HL
	DEC	A		; NORMAL END OF FILE?
	JP	NZ,UNKWN0
	CALL	RESETDR		; YES, RESET PREVIOUS DRIVE.
	CALL	CONVFST		; CONVERT THE FIRST FILE NAME THAT FOLLOWS
	LD	HL,CHGDRV	; COMMAND NAME.
	PUSH	HL
	LD	A,(HL)		; SET DRIVE CODE IN DEFAULT FCB.
	LD	(FCB),A
	LD	A,16		; PUT SECOND NAME 16 BYTES LATER.
	CALL	CONVERT		; CONVERT SECOND FILE NAME.
	POP	HL
	LD	A,(HL)		; AND SET THE DRIVE FOR THIS SECOND FILE.
	LD	(FCB+16),A
	XOR	A		; CLEAR RECORD BYTE IN FCB.
	LD	(FCB+32),A
	LD	DE,TFCB		; MOVE IT INTO PLACE AT(005CH).
	LD	HL,FCB
	LD	B,33
	CALL	HL2DE
	LD	HL,INBUFF+2	; NOW MOVE THE REMAINDER OF THE INPUT
UNKWN5:	LD	A,(HL)		; LINE DOWN TO (0080H). LOOK FOR A NON BLANK.
	OR	A		; OR A NULL.
	JP	Z,UNKWN6
	CP	' '
	JP	Z,UNKWN6
	INC	HL
	JP	UNKWN5
;
;   DO THE LINE MOVE NOW. IT ENDS IN A NULL BYTE.
;
UNKWN6:	LD	B,0		; KEEP A CHARACTER COUNT.
	LD	DE,TBUFF+1	; DATA GETS PUT HERE.
UNKWN7:	LD	A,(HL)		; MOVE IT NOW.
	LD	(DE),A
	OR	A
	JP	Z,UNKWN8
	INC	B
	INC	HL
	INC	DE
	JP	UNKWN7
UNKWN8:	LD	A,B		; NOW STORE THE CHARACTER COUNT.
	LD	(TBUFF),A
	CALL	CRLF		; CLEAN UP THE SCREEN.
	CALL	STDDMA		; SET STANDARD TRANSFER ADDRESS.
	CALL	SETCDRV		; RESET CURRENT DRIVE.
	CALL	TBASE		; AND EXECUTE THE PROGRAM.
;
;   TRANSIANT PROGRAMS RETURN HERE (OR REBOOT).
;
	LD	SP,BATCH	; SET STACK FIRST OFF.
	CALL	MOVECD		; MOVE CURRENT DRIVE INTO PLACE (TDRIVE).
	CALL	DSKSEL		; AND RESELECT IT.
	JP	CMMND1		; BACK TO COMAND MODE.
;
;   GET HERE IF SOME ERROR OCCURED.
;
UNKWN9:	CALL	RESETDR		; INPROPER FORMAT.
	JP	SYNERR
UNKWN0:	LD	BC,BADLOAD	; READ ERROR OR WON'T FIT.
	CALL	PLINE
	JP	GETBACK
BADLOAD:.DB "BAD LOAD",0
COMFILE:.DB "COM"		; COMMAND FILE EXTENSION.
;
;   GET HERE TO RETURN TO COMMAND LEVEL. WE WILL RESET THE
; PREVIOUS ACTIVE DRIVE AND THEN EITHER RETURN TO COMMAND
; LEVEL DIRECTLY OR PRINT ERROR MESSAGE AND THEN RETURN.
;
GETBACK:CALL	RESETDR		; RESET PREVIOUS DRIVE.
GETBACK1:
	CALL	CONVFST		; CONVERT FIRST NAME IN (FCB).
	LD	A,(FCB+1)	; IF THIS WAS JUST A DRIVE CHANGE REQUEST,
	SUB	' '		; MAKE SURE IT WAS VALID.
	LD	HL,CHGDRV
	OR	(HL)
	JP	NZ,SYNERR
	JP	CMMND1		; OK, RETURN TO COMMAND LEVEL.
;
;   CCP STACK AREA.
;
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
CCPSTACK:	.EQU $			; END OF CCP STACK AREA.
;
;   BATCH (OR SUBMIT) PROCESSING INFORMATION STORAGE.
;
BATCH:	.DB 0			; BATCH MODE FLAG (0=NOT ACTIVE).
; BATCHFCB:DB	0,'$$$     SUB',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
BATCHFCB:
	.DB 0,"$$$     SUB"
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;
;   FILE CONTROL BLOCK SETUP BY THE CCP.
;
;FCB	.DB	0,'           ',0,0,0,0,0,'           ',0,0,0,0,0
FCB:	.DB 0,"           ",0,0,0,0,0
	.DB "           ",0,0,0,0,0
RTNCODE:.DB 0			; STATUS RETURNED FROM BDOS CALL.
CDRIVE:	.DB 0			; CURRENTLY ACTIVE DRIVE.
CHGDRV:	.DB 0			; CHANGE IN DRIVES FLAG (0=NO CHANGE).
NBYTES:	.DW 0			; BYTE COUNTER USED BY TYPE.
;
;   ROOM FOR EXPANSION?
;
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0
;
;   NOTE THAT THE FOLLOWING SIX BYTES MUST MATCH THOSE AT
; (PATTRN1) OR CP/M WILL HALT. WHY?
;
PATTRN2:.DB 0,22,0,0,0,0	; (* SERIAL NUMBER BYTES *).
;
;**************************************************************
;*
;*                    B D O S   E N T R Y
;*
;**************************************************************
;
FBASE:	JP	FBASE1
;
;   BDOS ERROR TABLE.
;
BADSCTR:.DW ERROR1		; BAD SECTOR ON READ OR WRITE.
BADSLCT:.DW ERROR2		; BAD DISK SELECT.
RODISK:	.DW ERROR3		; DISK IS READ ONLY.
ROFILE:	.DW ERROR4		; FILE IS READ ONLY.
;
;   ENTRY INTO BDOS. (DE) OR (E) ARE THE PARAMETERS PASSED. THE
; FUNCTION NUMBER DESIRED IS IN REGISTER (C).
;
FBASE1:	EX	DE,HL		; SAVE THE (DE) PARAMETERS.
	LD	(PARAMS),HL
	EX	DE,HL
	LD	A,E		; AND SAVE REGISTER (E) IN PARTICULAR.
	LD	(EPARAM),A
	LD	HL,0
	LD	(STATUS),HL	; CLEAR RETURN STATUS.
	ADD	HL,SP
	LD	(USRSTACK),HL	; SAVE USERS STACK POINTER.
	LD	SP,STKAREA	; AND SET OUR OWN.
	XOR	A		; CLEAR AUTO SELECT STORAGE SPACE.
	LD	(AUTOFLAG),A
	LD	(AUTO),A
	LD	HL,GOBACK	; SET RETURN ADDRESS.
	PUSH	HL
	LD	A,C		; GET FUNCTION NUMBER.
	CP	NFUNCTS		; VALID FUNCTION NUMBER?
	RET	NC
	LD	C,E		; KEEP SINGLE REGISTER FUNCTION HERE.
	LD	HL,FUNCTNS	; NOW LOOK THRU THE FUNCTION TABLE.
	LD	E,A
	LD	D,0		; (DE)=FUNCTION NUMBER.
	ADD	HL,DE
	ADD	HL,DE		; (HL)=(START OF TABLE)+2*(FUNCTION NUMBER).
	LD	E,(HL)
	INC	HL
	LD	D,(HL)		; NOW (DE)=ADDRESS FOR THIS FUNCTION.
	LD	HL,(PARAMS)	; RETRIEVE PARAMETERS.
	EX	DE,HL		; NOW (DE) HAS THE ORIGINAL PARAMETERS.
	JP	(HL)		; EXECUTE DESIRED FUNCTION.
;
;   BDOS FUNCTION JUMP TABLE.
;
NFUNCTS:.EQU 41			; NUMBER OF FUNCTIONS IN FOLLOWIN TABLE.
;
FUNCTNS:.DW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB
	.DW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL
	.DW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE
	.DW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR
	.DW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN
	.DW RTN,WTSPECL
;
;   BDOS ERROR MESSAGE SECTION.
;
ERROR1:	LD	HL,BADSEC	; BAD SECTOR MESSAGE.
	CALL	PRTERR		; PRINT IT AND GET A 1 CHAR RESPONCE.
	CP	CNTRLC		; RE-BOOT REQUEST (CONTROL-C)?
	JP	Z,0		; YES.
	RET			; NO, RETURN TO RETRY I/O FUNCTION.
;
ERROR2:	LD	HL,BADSEL	; BAD DRIVE SELECTED.
	JP	ERROR5
;
ERROR3:	LD	HL,DISKRO	; DISK IS READ ONLY.
	JP	ERROR5
;
ERROR4:	LD	HL,FILERO	; FILE IS READ ONLY.
;
ERROR5:	CALL	PRTERR
	JP	0		; ALWAYS REBOOT ON THESE ERRORS.
;
BDOSERR:.DB "BDOS ERR ON "
BDOSDRV:.DB " : $"
BADSEC:	.DB "BAD SECTOR$"
BADSEL:	.DB "SELECT$"		; reboot if a bad drive select
FILERO:	.DB "FILE "
DISKRO:	.DB "R/O$"
;
;   PRINT BDOS ERROR MESSAGE.
;
PRTERR:	PUSH	HL		; SAVE SECOND MESSAGE POINTER.
	CALL	OUTCRLF		; SEND (CR)(LF).
	LD	A,(ACTIVE)	; GET ACTIVE DRIVE.
	ADD	A,'A'		; MAKE ASCII.
	LD	(BDOSDRV),A	; AND PUT IN MESSAGE.
	LD	BC,BDOSERR	; AND PRINT IT.
	CALL	PRTMESG
	POP	BC		; PRINT SECOND MESSAGE LINE NOW.
	CALL	PRTMESG

;
;   GET AN INPUT CHARACTER. WE WILL CHECK OUR 1 CHARACTER
; BUFFER FIRST. THIS MAY BE SET BY THE CONSOLE STATUS ROUTINE.
;
GETCHAR:LD	HL,CHARBUF	; CHECK CHARACTER BUFFER.
	LD	A,(HL)		; ANYTHING PRESENT ALREADY?
	LD	(HL),0		; ...EITHER CASE CLEAR IT.
	OR	A
	RET	NZ		; YES, USE IT.
	JP	CONIN		; NOPE, GO GET A CHARACTER RESPONCE.
;
;   INPUT AND ECHO A CHARACTER.
;
GETECHO:CALL	GETCHAR		; INPUT A CHARACTER.
	CALL	CHKCHAR		; CARRIAGE CONTROL?
	RET	C		; NO, A REGULAR CONTROL CHAR SO DON'T ECHO.
	PUSH	AF		; OK, SAVE CHARACTER NOW.
	LD	C,A
	CALL	OUTCON		; AND ECHO IT.
	POP	AF		; GET CHARACTER AND RETURN.
	RET
;
;   CHECK CHARACTER IN (A). SET THE ZERO FLAG ON A CARRIAGE
; CONTROL CHARACTER AND THE CARRY FLAG ON ANY OTHER CONTROL
; CHARACTER.
;
CHKCHAR:CP	CR		; CHECK FOR CARRIAGE RETURN, LINE FEED, BACKSPACE,
	RET	Z		; OR A TAB.
	CP	LF
	RET	Z
	CP	TAB
	RET	Z
	CP	BS
	RET	Z
	CP	' '		; OTHER CONTROL CHAR? SET CARRY FLAG.
	RET
;
;   CHECK THE CONSOLE DURING OUTPUT. HALT ON A CONTROL-S, THEN
; REBOOT ON A CONTROL-C. IF ANYTHING ELSE IS READY, CLEAR THE
; ZERO FLAG AND RETURN (THE CALLING ROUTINE MAY WANT TO DO
; SOMETHING).
;
CKCONSOL:
	LD	A,(CHARBUF)	; CHECK BUFFER.
	OR	A		; IF ANYTHING, JUST RETURN WITHOUT CHECKING.
	JP	NZ,CKCON2
	CALL	CONST		; NOTHING IN BUFFER. CHECK CONSOLE.
	AND	01H		; LOOK AT BIT 0.
	RET	Z		; RETURN IF NOTHING.
	CALL	CONIN		; OK, GET IT.
	CP	CNTRLS		; IF NOT CONTROL-S, RETURN WITH ZERO CLEARED.
	JP	NZ,CKCON1
	CALL	CONIN		; HALT PROCESSING UNTIL ANOTHER CHAR
	CP	CNTRLC		; IS TYPED. CONTROL-C?
	JP	Z,0		; YES, REBOOT NOW.
	XOR	A		; NO, JUST PRETEND NOTHING WAS EVER READY.
	RET
CKCON1:	LD	(CHARBUF),A	; SAVE CHARACTER IN BUFFER FOR LATER PROCESSING.
CKCON2:	LD	A,1		; SET (A) TO NON ZERO TO MEAN SOMETHING IS READY.
	RET
;
;   OUTPUT (C) TO THE SCREEN. IF THE PRINTER FLIP-FLOP FLAG
; IS SET, WE WILL SEND CHARACTER TO PRINTER ALSO. THE CONSOLE
; WILL BE CHECKED IN THE PROCESS.
;
OUTCHAR:LD	A,(OUTFLAG)	; CHECK OUTPUT FLAG.
	OR	A		; ANYTHING AND WE WON'T GENERATE OUTPUT.
	JP	NZ,OUTCHR1
	PUSH	BC
	CALL	CKCONSOL	; CHECK CONSOLE (WE DON'T CARE WHATS THERE).
	POP	BC
	PUSH	BC
	CALL	CONOUT		; OUTPUT (C) TO THE SCREEN.
	POP	BC
	PUSH	BC
	LD	A,(PRTFLAG)	; CHECK PRINTER FLIP-FLOP FLAG.
	OR	A
	CALL	NZ,LIST		; PRINT IT ALSO IF NON-ZERO.
	POP	BC
OUTCHR1:LD	A,C		; UPDATE CURSORS POSITION.
	LD	HL,CURPOS
	CP	DEL		; RUBOUTS DON'T DO ANYTHING HERE.
	RET	Z
	INC	(HL)		; BUMP LINE POINTER.
	CP	' '		; AND RETURN IF A NORMAL CHARACTER.
	RET	NC
	DEC	(HL)		; RESTORE AND CHECK FOR THE START OF THE LINE.
	LD	A,(HL)
	OR	A
	RET	Z		; INGNORE CONTROL CHARACTERS AT THE START OF THE LINE.
	LD	A,C
	CP	BS		; IS IT A BACKSPACE?
	JP	NZ,OUTCHR2
	DEC	(HL)		; YES, BACKUP POINTER.
	RET
OUTCHR2:CP	LF		; IS IT A LINE FEED?
	RET	NZ		; IGNORE ANYTHING ELSE.
	LD	(HL),0		; RESET POINTER TO START OF LINE.
	RET
;
;   OUTPUT (A) TO THE SCREEN. IF IT IS A CONTROL CHARACTER
; (OTHER THAN CARRIAGE CONTROL), USE ^X FORMAT.
;
SHOWIT:	LD	A,C
	CALL	CHKCHAR		; CHECK CHARACTER.
	JP	NC,OUTCON	; NOT A CONTROL, USE NORMAL OUTPUT.
	PUSH	AF
	LD	C,'^'		; FOR A CONTROL CHARACTER, PRECEED IT WITH '^'.
	CALL	OUTCHAR
	POP	AF
	OR	'@'		; AND THEN USE THE LETTER .EQUIVELANT.
	LD	C,A
;
;   FUNCTION TO OUTPUT (C) TO THE CONSOLE DEVICE AND EXPAND TABS
; IF NECESSARY.
;
OUTCON:	LD	A,C
	CP	TAB		; IS IT A TAB?
	JP	NZ,OUTCHAR	; USE REGULAR OUTPUT.
OUTCON1:LD	C,' '		; YES IT IS, USE SPACES INSTEAD.
	CALL	OUTCHAR
	LD	A,(CURPOS)	; GO UNTIL THE CURSOR IS AT A MULTIPLE OF 8

	AND	07H		; POSITION.
	JP	NZ,OUTCON1
	RET
;
;   ECHO A BACKSPACE CHARACTER. ERASE THE PREVOIUS CHARACTER
; ON THE SCREEN.
;
BACKUP:	CALL	BACKUP1		; BACKUP THE SCREEN 1 PLACE.
	LD	C,' '		; THEN BLANK THAT CHARACTER.
	CALL	CONOUT
BACKUP1:LD	C,BS		; THEN BACK SPACE ONCE MORE.
	JP	CONOUT
;
;   SIGNAL A DELETED LINE. PRINT A '#' AT THE END AND START
; OVER.
;
NEWLINE:LD	C,'#'
	CALL	OUTCHAR		; PRINT THIS.
	CALL	OUTCRLF		; START NEW LINE.
NEWLN1:	LD	A,(CURPOS)	; MOVE THE CURSOR TO THE STARTING POSITION.
	LD	HL,STARTING
	CP	(HL)
	RET	NC		; THERE YET?
	LD	C,' '
	CALL	OUTCHAR		; NOPE, KEEP GOING.
	JP	NEWLN1
;
;   OUTPUT A (CR) (LF) TO THE CONSOLE DEVICE (SCREEN).
;
OUTCRLF:LD	C,CR
	CALL	OUTCHAR
	LD	C,LF
	JP	OUTCHAR
;
;   PRINT MESSAGE POINTED TO BY (BC). IT WILL END WITH A '$'.
;
PRTMESG:LD	A,(BC)		; CHECK FOR TERMINATING CHARACTER.
	CP	'$'
	RET	Z
	INC	BC
	PUSH	BC		; OTHERWISE, BUMP POINTER AND PRINT IT.
	LD	C,A
	CALL	OUTCON
	POP	BC
	JP	PRTMESG
;
;   FUNCTION TO EXECUTE A BUFFERED READ.
;
RDBUFF:	LD	A,(CURPOS)	; USE PRESENT LOCATION AS STARTING ONE.
	LD	(STARTING),A
	LD	HL,(PARAMS)	; GET THE MAXIMUM BUFFER SPACE.
	LD	C,(HL)
	INC	HL		; POINT TO FIRST AVAILABLE SPACE.
	PUSH	HL		; AND SAVE.
	LD	B,0		; KEEP A CHARACTER COUNT.
RDBUF1:	PUSH	BC
	PUSH	HL
RDBUF2:	CALL	GETCHAR		; GET THE NEXT INPUT CHARACTER.
	AND	7FH		; STRIP BIT 7.
	POP	HL		; RESET REGISTERS.
	POP	BC
	CP	CR		; EN OF THE LINE?
	JP	Z,RDBUF17
	CP	LF
	JP	Z,RDBUF17
	CP	BS		; HOW ABOUT A BACKSPACE?
	JP	NZ,RDBUF3
	LD	A,B		; YES, BUT IGNORE AT THE BEGINNING OF THE LINE.
	OR	A
	JP	Z,RDBUF1
	DEC	B		; OK, UPDATE COUNTER.
	LD	A,(CURPOS)	; IF WE BACKSPACE TO THE START OF THE LINE,
	LD	(OUTFLAG),A	; TREAT AS A CANCEL (CONTROL-X).
	JP	RDBUF10
RDBUF3:	CP	DEL		; USER TYPED A RUBOUT?
	JP	NZ,RDBUF4
	LD	A,B		; IGNORE AT THE START OF THE LINE.
	OR	A
	JP	Z,RDBUF1
	LD	A,(HL)		; OK, ECHO THE PREVOIUS CHARACTER.
	DEC	B		; AND RESET POINTERS (COUNTERS).
	DEC	HL
	JP	RDBUF15
RDBUF4:	CP	CNTRLE		; PHYSICAL END OF LINE?
	JP	NZ,RDBUF5
	PUSH	BC		; YES, DO IT.
	PUSH	HL
	CALL	OUTCRLF
	XOR	A		; AND UPDATE STARTING POSITION.
	LD	(STARTING),A
	JP	RDBUF2
RDBUF5:	CP	CNTRLP		; CONTROL-P?
	JP	NZ,RDBUF6
	PUSH	HL		; YES, FLIP THE PRINT FLAG FILP-FLOP BYTE.
	LD	HL,PRTFLAG
	LD	A,1		; PRTFLAG=1-PRTFLAG
	SUB	(HL)
	LD	(HL),A
	POP	HL
	JP	RDBUF1
RDBUF6:	CP	CNTRLX		; CONTROL-X (CANCEL)?
	JP	NZ,RDBUF8
	POP	HL
RDBUF7:	LD	A,(STARTING)	; YES, BACKUP THE CURSOR TO HERE.
	LD	HL,CURPOS
	CP	(HL)
	JP	NC,RDBUFF	; DONE YET?
	DEC	(HL)		; NO, DECREMENT POINTER AND OUTPUT BACK UP ONE SPACE.
	CALL	BACKUP
	JP	RDBUF7
RDBUF8:	CP	CNTRLU		; CNTROL-U (CANCEL LINE)?
	JP	NZ,RDBUF9
	CALL	NEWLINE		; START A NEW LINE.
	POP	HL
	JP	RDBUFF
RDBUF9:	CP	CNTRLR		; CONTROL-R?
	JP	NZ,RDBUF14
RDBUF10:PUSH	BC		; YES, START A NEW LINE AND RETYPE THE OLD ONE.
	CALL	NEWLINE
	POP	BC
	POP	HL
	PUSH	HL
	PUSH	BC
RDBUF11:LD	A,B		; DONE WHOLE LINE YET?
	OR	A
	JP	Z,RDBUF12
	INC	HL		; NOPE, GET NEXT CHARACTER.
	LD	C,(HL)
	DEC	B		; COUNT IT.
	PUSH	BC
	PUSH	HL
	CALL	SHOWIT		; AND DISPLAY IT.
	POP	HL
	POP	BC
	JP	RDBUF11
RDBUF12:PUSH	HL		; DONE WITH LINE. IF WE WERE DISPLAYING
	LD	A,(OUTFLAG)	; THEN UPDATE CURSOR POSITION.
	OR	A
	JP	Z,RDBUF2
	LD	HL,CURPOS	; BECAUSE THIS LINE IS SHORTER, WE MUST
	SUB	(HL)		; BACK UP THE CURSOR (NOT THE SCREEN HOWEVER)
	LD	(OUTFLAG),A	; SOME NUMBER OF POSITIONS.
RDBUF13:CALL	BACKUP		; NOTE THAT AS LONG AS (OUTFLAG) IS NON
	LD	HL,OUTFLAG	; ZERO, THE SCREEN WILL NOT BE CHANGED.
	DEC	(HL)
	JP	NZ,RDBUF13
	JP	RDBUF2		; NOW JUST GET THE NEXT CHARACTER.
;
;   JUST A NORMAL CHARACTER, PUT THIS IN OUR BUFFER AND ECHO.
;
RDBUF14:INC	HL
	LD	(HL),A		; STORE CHARACTER.
	INC	B		; AND COUNT IT.
RDBUF15:PUSH	BC
	PUSH	HL
	LD	C,A		; ECHO IT NOW.
	CALL	SHOWIT
	POP	HL
	POP	BC
	LD	A,(HL)		; WAS IT AN ABORT REQUEST?
	CP	CNTRLC		; CONTROL-C ABORT?
	LD	A,B
	JP	NZ,RDBUF16
	CP	1		; ONLY IF AT START OF LINE.
	JP	Z,0
RDBUF16:CP	C		; NOPE, HAVE WE FILLED THE BUFFER?
	JP	C,RDBUF1
RDBUF17:POP	HL		; YES END THE LINE AND RETURN.
	LD	(HL),B
	LD	C,CR
	JP	OUTCHAR		; OUTPUT (CR) AND RETURN.
;
;   FUNCTION TO GET A CHARACTER FROM THE CONSOLE DEVICE.
;
GETCON:	CALL	GETECHO		; GET AND ECHO.
	JP	SETSTAT		; SAVE STATUS AND RETURN.
;
;   FUNCTION TO GET A CHARACTER FROM THE TAPE READER DEVICE.
;
GETRDR:	CALL	READER		; GET A CHARACTER FROM READER, SET STATUS AND RETURN.
	JP	SETSTAT
;
;  FUNCTION TO PERFORM DIRECT CONSOLE I/O. IF (C) CONTAINS (FF)
; THEN THIS IS AN INPUT REQUEST. IF (C) CONTAINS (FE) THEN
; THIS IS A STATUS REQUEST. OTHERWISE WE ARE TO OUTPUT (C).
;
DIRCIO:	LD	A,C		; TEST FOR (FF).
	INC	A
	JP	Z,DIRC1
	INC	A		; TEST FOR (FE).
	JP	Z,CONST
	JP	CONOUT		; JUST OUTPUT (C).
DIRC1:	CALL	CONST		; THIS IS AN INPUT REQUEST.
	OR	A
	JP	Z,GOBACK1	; NOT READY? JUST RETURN (DIRECTLY).
	CALL	CONIN		; YES, GET CHARACTER.
	JP	SETSTAT		; SET STATUS AND RETURN.
;
;   FUNCTION TO RETURN THE I/O BYTE.
;
GETIOB:	LD	A,(IOBYTE)
	JP	SETSTAT
;
;   FUNCTION TO SET THE I/O BYTE.
;
SETIOB:	LD	HL,IOBYTE
	LD	(HL),C
	RET
;
;   FUNCTION TO PRINT THE CHARACTER STRING POINTED TO BY (DE)
; ON THE CONSOLE DEVICE. THE STRING ENDS WITH A '$'.
;
PRTSTR:	EX	DE,HL
	LD	C,L
	LD	B,H		; NOW (BC) POINTS TO IT.
	JP	PRTMESG
;
;   FUNCTION TO INTERIGATE THE CONSOLE DEVICE.
;
GETCSTS:CALL	CKCONSOL
;
;   GET HERE TO SET THE STATUS AND RETURN TO THE CLEANUP
; SECTION. THEN BACK TO THE USER.
;
SETSTAT:LD	(STATUS),A
RTN:	RET
;
;   SET THE STATUS TO 1 (READ OR WRITE ERROR CODE).
;
IOERR1:	LD	A,1
	JP	SETSTAT
;
OUTFLAG:.DB 0			; OUTPUT FLAG (NON ZERO MEANS NO OUTPUT).
STARTING:
	.DB 2			; STARTING POSITION FOR CURSOR.
CURPOS:	.DB 0			; CURSOR POSITION (0=START OF LINE).
PRTFLAG:.DB 0			; PRINTER FLAG (CONTROL-P TOGGLE). LIST IF NON ZERO.
CHARBUF:.DB 0			; SINGLE INPUT CHARACTER BUFFER.
;
;   STACK AREA FOR BDOS CALLS.
;
USRSTACK:
	.DW 0			; SAVE USERS STACK POINTER HERE.
;
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
STKAREA:.EQU $			; END OF STACK AREA.
;
USERNO:	.DB 0			; CURRENT USER NUMBER.
ACTIVE:	.DB 0			; CURRENTLY ACTIVE DRIVE.
PARAMS:	.DW 0			; SAVE (DE) PARAMETERS HERE ON ENTRY.
STATUS:	.DW 0			; STATUS RETURNED FROM BDOS FUNCTION.
;
;   SELECT ERROR OCCURED, JUMP TO ERROR ROUTINE.
;
SLCTERR:LD	HL,BADSLCT
;
;   JUMP TO (HL) INDIRECTLY.
;
JUMPHL:	LD	E,(HL)
	INC	HL
	LD	D,(HL)		; NOW (DE) CONTAIN THE DESIRED ADDRESS.
	EX	DE,HL
	JP	(HL)
;
;   BLOCK MOVE. (DE) TO (HL), (C) BYTES TOTAL.
;
DE2HL:	INC	C		; IS COUNT DOWN TO ZERO?
DE2HL1:	DEC	C
	RET	Z		; YES, WE ARE DONE.
	LD	A,(DE)		; NO, MOVE ONE MORE BYTE.
	LD	(HL),A
	INC	DE
	INC	HL
	JP	DE2HL1		; AND REPEAT.
;
;   SELECT THE DESIRED DRIVE.
;
SELECT:	LD	A,(ACTIVE)	; GET ACTIVE DISK.
	LD	C,A
	CALL	SELDSK		; SELECT IT.
	LD	A,H		; VALID DRIVE?
	OR	L		; VALID DRIVE?
	RET	Z		; RETURN IF NOT.
;
;   HERE, THE BIOS RETURNED THE ADDRESS OF THE PARAMETER BLOCK
; IN (HL). WE WILL EXTRACT THE NECESSARY POINTERS AND SAVE THEM.
;
	LD	E,(HL)		; YES, GET ADDRESS OF TRANSLATION TABLE INTO (DE).
	INC	HL
	LD	D,(HL)
	INC	HL
	LD	(SCRATCH1),HL	; SAVE POINTERS TO SCRATCH AREAS.
	INC	HL
	INC	HL
	LD	(SCRATCH2),HL	; DITTO.
	INC	HL
	INC	HL
	LD	(SCRATCH3),HL	; DITTO.
	INC	HL
	INC	HL
	EX	DE,HL		; NOW SAVE THE TRANSLATION TABLE ADDRESS.
	LD	(XLATE),HL
	LD	HL,DIRBUF	; PUT THE NEXT 8 BYTES HERE.
	LD	C,8		; THEY CONSIST OF THE DIRECTORY BUFFER
	CALL	DE2HL		; POINTER, PARAMETER BLOCK POINTER,
	LD	HL,(DISKPB)	; CHECK AND ALLOCATION VECTORS.
	EX	DE,HL
	LD	HL,SECTORS	; MOVE PARAMETER BLOCK INTO OUR RAM.
	LD	C,15		; IT IS 15 BYTES LONG.
	CALL	DE2HL
	LD	HL,(DSKSIZE)	; CHECK DISK SIZE.
	LD	A,H		; MORE THAN 256 BLOCKS ON THIS?
	LD	HL,BIGDISK
	LD	(HL),0FFH	; SET TO SAMLL.
	OR	A
	JP	Z,SELECT1
	LD	(HL),0		; WRONG, SET TO LARGE.
SELECT1:LD	A,0FFH		; CLEAR THE ZERO FLAG.
	OR	A
	RET
;
;   ROUTINE TO HOME THE DISK TRACK HEAD AND CLEAR POINTERS.
;
HOMEDRV:CALL	HOME		; HOME THE HEAD.
	XOR	A
	LD	HL,(SCRATCH2)	; SET OUR TRACK POINTER ALSO.
	LD	(HL),A
	INC	HL
	LD	(HL),A
	LD	HL,(SCRATCH3)	; AND OUR SECTOR POINTER.
	LD	(HL),A
	INC	HL
	LD	(HL),A
	RET
;
;   DO THE ACTUAL DISK READ AND CHECK THE ERROR RETURN STATUS.
;
DOREAD:	CALL	READ
	JP	IORET
;
;   DO THE ACTUAL DISK WRITE AND HANDLE ANY BIOS ERROR.
;
DOWRITE:CALL	WRITE
IORET:	OR	A
	RET	Z		; RETURN UNLESS AN ERROR OCCURED.
	LD	HL,BADSCTR	; BAD READ/WRITE ON THIS SECTOR.
	JP	JUMPHL
;
;   ROUTINE TO SELECT THE TRACK AND SECTOR THAT THE DESIRED
; BLOCK NUMBER FALLS IN.
;
TRKSEC:	LD	HL,(FILEPOS)	; GET POSITION OF LAST ACCESSED FILE
	LD	C,2		; IN DIRECTORY AND COMPUTE SECTOR #.
	CALL	SHIFTR		; SECTOR #=FILE-POSITION/4.
	LD	(BLKNMBR),HL	; SAVE THIS AS THE BLOCK NUMBER OF INTEREST.
	LD	(CKSUMTBL),HL	; WHAT'S IT DOING HERE TOO?
;
;   IF THE SECTOR NUMBER HAS ALREADY BEEN SET (BLKNMBR), ENTER
; AT THIS POINT.
;
TRKSEC1:LD	HL,BLKNMBR
	LD	C,(HL)		; MOVE SECTOR NUMBER INTO (BC).
	INC	HL
	LD	B,(HL)
	LD	HL,(SCRATCH3)	; GET CURRENT SECTOR NUMBER AND
	LD	E,(HL)		; MOVE THIS INTO (DE).
	INC	HL
	LD	D,(HL)
	LD	HL,(SCRATCH2)	; GET CURRENT TRACK NUMBER.
	LD	A,(HL)		; AND THIS INTO (HL).
	INC	HL
	LD	H,(HL)
	LD	L,A
TRKSEC2:LD	A,C		; IS DESIRED SECTOR BEFORE CURRENT ONE?
	SUB	E
	LD	A,B
	SBC	A,D
	JP	NC,TRKSEC3
	PUSH	HL		; YES, DECREMENT SECTORS BY ONE TRACK.
	LD	HL,(SECTORS)	; GET SECTORS PER TRACK.
	LD	A,E
	SUB	L
	LD	E,A
	LD	A,D
	SBC	A,H
	LD	D,A		; NOW WE HAVE BACKED UP ONE FULL TRACK.
	POP	HL
	DEC	HL		; ADJUST TRACK COUNTER.
	JP	TRKSEC2
TRKSEC3:PUSH	HL		; DESIRED SECTOR IS AFTER CURRENT ONE.
	LD	HL,(SECTORS)	; GET SECTORS PER TRACK.
	ADD	HL,DE		; BUMP SECTOR POINTER TO NEXT TRACK.
	JP	C,TRKSEC4
	LD	A,C		; IS DESIRED SECTOR NOW BEFORE CURRENT ONE?
	SUB	L
	LD	A,B
	SBC	A,H
	JP	C,TRKSEC4
	EX	DE,HL		; NOT YES, INCREMENT TRACK COUNTER
	POP	HL		; AND CONTINUE UNTIL IT IS.
	INC	HL
	JP	TRKSEC3
;
;   HERE WE HAVE DETERMINED THE TRACK NUMBER THAT CONTAINS THE
; DESIRED SECTOR.
;
TRKSEC4:POP	HL		; GET TRACK NUMBER (HL).
	PUSH	BC
	PUSH	DE
	PUSH	HL
	EX	DE,HL
	LD	HL,(OFFSET)	; ADJUST FOR FIRST TRACK OFFSET.
	ADD	HL,DE
	LD	B,H
	LD	C,L
	CALL	SETTRK		; SELECT THIS TRACK.
	POP	DE		; RESET CURRENT TRACK POINTER.
	LD	HL,(SCRATCH2)
	LD	(HL),E
	INC	HL
	LD	(HL),D
	POP	DE
	LD	HL,(SCRATCH3)	; RESET THE FIRST SECTOR ON THIS TRACK.
	LD	(HL),E
	INC	HL
	LD	(HL),D
	POP	BC
	LD	A,C		; NOW SUBTRACT THE DESIRED ONE.
	SUB	E		; TO MAKE IT RELATIVE (1-# SECTORS/TRACK).
	LD	C,A
	LD	A,B
	SBC	A,D
	LD	B,A
	LD	HL,(XLATE)	; TRANSLATE THIS SECTOR ACCORDING TO THIS TABLE.
	EX	DE,HL
	CALL	SECTRN		; LET THE BIOS TRANSLATE IT.
	LD	C,L
	LD	B,H
	JP	SETSEC		; AND SELECT IT.
;
;   COMPUTE BLOCK NUMBER FROM RECORD NUMBER (SAVNREC) AND
; EXTENT NUMBER (SAVEXT).
;
GETBLOCK:
	LD	HL,BLKSHFT	; GET LOGICAL TO PHYSICAL CONVERSION.
	LD	C,(HL)		; NOTE THAT THIS IS BASE 2 LOG OF RATIO.
	LD	A,(SAVNREC)	; GET RECORD NUMBER.
GETBLK1:OR	A		; COMPUTE (A)=(A)/2^BLKSHFT.
	RRA
	DEC	C
	JP	NZ,GETBLK1
	LD	B,A		; SAVE RESULT IN (B).
	LD	A,8
	SUB	(HL)
	LD	C,A		; COMPUTE (C)=8-BLKSHFT.
	LD	A,(SAVEXT)
GETBLK2:DEC	C		; COMPUTE (A)=SAVEXT*2^(8-BLKSHFT).
	JP	Z,GETBLK3
	OR	A
	RLA
	JP	GETBLK2
GETBLK3:ADD	A,B
	RET
;
;   ROUTINE TO EXTRACT THE (BC) BLOCK BYTE FROM THE FCB POINTED
; TO BY (PARAMS). IF THIS IS A BIG-DISK, THEN THESE ARE 16 BIT
; BLOCK NUMBERS, ELSE THEY ARE 8 BIT NUMBERS.
; NUMBER IS RETURNED IN (HL).
;
EXTBLK:	LD	HL,(PARAMS)	; GET FCB ADDRESS.
	LD	DE,16		; BLOCK NUMBERS START 16 BYTES INTO FCB.
	ADD	HL,DE
	ADD	HL,BC
	LD	A,(BIGDISK)	; ARE WE USING A BIG-DISK?
	OR	A
	JP	Z,EXTBLK1
	LD	L,(HL)		; NO, EXTRACT AN 8 BIT NUMBER FROM THE FCB.
	LD	H,0
	RET
EXTBLK1:ADD	HL,BC		; YES, EXTRACT A 16 BIT NUMBER.
	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	EX	DE,HL		; RETURN IN (HL).
	RET
;
;   COMPUTE BLOCK NUMBER.
;
COMBLK:	CALL	GETBLOCK
	LD	C,A
	LD	B,0
	CALL	EXTBLK
	LD	(BLKNMBR),HL
	RET
;
;   CHECK FOR A ZERO BLOCK NUMBER (UNUSED).
;
CHKBLK:	LD	HL,(BLKNMBR)
	LD	A,L		; IS IT ZERO?
	OR	H
	RET
;
;   ADJUST PHYSICAL BLOCK (BLKNMBR) AND CONVERT TO LOGICAL
; SECTOR (LOGSECT). THIS IS THE STARTING SECTOR OF THIS BLOCK.
; THE ACTUAL SECTOR OF INTEREST IS THEN ADDED TO THIS AND THE
; RESULTING SECTOR NUMBER IS STORED BACK IN (BLKNMBR). THIS
; WILL STILL HAVE TO BE ADJUSTED FOR THE TRACK NUMBER.
;
LOGICAL:LD	A,(BLKSHFT)	; GET LOG2(PHYSICAL/LOGICAL SECTORS).
	LD	HL,(BLKNMBR)	; GET PHYSICAL SECTOR DESIRED.
LOGICL1:ADD	HL,HL		; COMPUTE LOGICAL SECTOR NUMBER.
	DEC	A		; NOTE LOGICAL SECTORS ARE 128 BYTES LONG.
	JP	NZ,LOGICL1
	LD	(LOGSECT),HL	; SAVE LOGICAL SECTOR.
	LD	A,(BLKMASK)	; GET BLOCK MASK.
	LD	C,A
	LD	A,(SAVNREC)	; GET NEXT SECTOR TO ACCESS.
	AND	C		; EXTRACT THE RELATIVE POSITION WITHIN PHYSICAL BLOCK.
	OR	L		; AND ADD IT TOO LOGICAL SECTOR.
	LD	L,A
	LD	(BLKNMBR),HL	; AND STORE.
	RET
;
;   SET (HL) TO POINT TO EXTENT BYTE IN FCB.
;
SETEXT:	LD	HL,(PARAMS)
	LD	DE,12		; IT IS THE TWELTH BYTE.
	ADD	HL,DE
	RET
;
;   SET (HL) TO POINT TO RECORD COUNT BYTE IN FCB AND (DE) TO
; NEXT RECORD NUMBER BYTE.
;
SETHLDE:LD	HL,(PARAMS)
	LD	DE,15		; RECORD COUNT BYTE (#15).
	ADD	HL,DE
	EX	DE,HL
	LD	HL,17		; NEXT RECORD NUMBER (#32).
	ADD	HL,DE
	RET
;
;   SAVE CURRENT FILE DATA FROM FCB.
;
STRDATA:CALL	SETHLDE
	LD	A,(HL)		; GET AND STORE RECORD COUNT BYTE.
	LD	(SAVNREC),A
	EX	DE,HL
	LD	A,(HL)		; GET AND STORE NEXT RECORD NUMBER BYTE.
	LD	(SAVNXT),A
	CALL	SETEXT		; POINT TO EXTENT BYTE.
	LD	A,(EXTMASK)	; GET EXTENT MASK.
	AND	(HL)
	LD	(SAVEXT),A	; AND SAVE EXTENT HERE.
	RET
;
;   SET THE NEXT RECORD TO ACCESS. IF (MODE) IS SET TO 2, THEN
; THE LAST RECORD BYTE (SAVNREC) HAS THE CORRECT NUMBER TO ACCESS.
; FOR S.EQUENTIAL ACCESS, (MODE) WILL BE .EQUAL TO 1.
;
SETNREC:CALL	SETHLDE
	LD	A,(MODE)	; GET S.EQUENTIAL FLAG (=1).
	CP	2		; A 2 INDICATES THAT NO ADDER IS NEEDED.
	JP	NZ,STNREC1
	XOR	A		; CLEAR ADDER (RANDOM ACCESS?).
STNREC1:LD	C,A
	LD	A,(SAVNREC)	; GET LAST RECORD NUMBER.
	ADD	A,C		; INCREMENT RECORD COUNT.
	LD	(HL),A		; AND SET FCB'S NEXT RECORD BYTE.
	EX	DE,HL
	LD	A,(SAVNXT)	; GET NEXT RECORD BYTE FROM STORAGE.
	LD	(HL),A		; AND PUT THIS INTO FCB AS NUMBER OF RECORDS USED.
	RET
;
;   SHIFT (HL) RIGHT (C) BITS.
;
SHIFTR:	INC	C
SHIFTR1:DEC	C
	RET	Z
	LD	A,H
	OR	A
	RRA
	LD	H,A
	LD	A,L
	RRA
	LD	L,A
	JP	SHIFTR1
;
;   COMPUTE THE CHECK-SUM FOR THE DIRECTORY BUFFER. RETURN
; INTEGER SUM IN (A).
;
CHECKSUM:
	LD	C,128		; LENGTH OF BUFFER.
	LD	HL,(DIRBUF)	; GET ITS LOCATION.
	XOR	A		; CLEAR SUMMATION BYTE.
CHKSUM1:ADD	A,(HL)		; AND COMPUTE SUM IGNORING CARRIES.
	INC	HL
	DEC	C
	JP	NZ,CHKSUM1
	RET
;
;   SHIFT (HL) LEFT (C) BITS.
;
SHIFTL:	INC	C
SHIFTL1:DEC	C
	RET	Z
	ADD	HL,HL		; SHIFT LEFT 1 BIT.
	JP	SHIFTL1
;
;   ROUTINE TO SET A BIT IN A 16 BIT VALUE CONTAINED IN (BC).
; THE BIT SET DEPENDS ON THE CURRENT DRIVE SELECTION.
;
SETBIT:	PUSH	BC		; SAVE 16 BIT WORD.
	LD	A,(ACTIVE)	; GET ACTIVE DRIVE.
	LD	C,A
	LD	HL,1
	CALL	SHIFTL		; SHIFT BIT 0 INTO PLACE.
	POP	BC		; NOW 'OR' THIS WITH THE ORIGINAL WORD.
	LD	A,C
	OR	L
	LD	L,A		; LOW BYTE DONE, DO HIGH BYTE.
	LD	A,B
	OR	H
	LD	H,A
	RET
;
;   EXTRACT THE WRITE PROTECT STATUS BIT FOR THE CURRENT DRIVE.
; THE RESULT IS RETURNED IN (A), BIT 0.
;
GETWPRT:LD	HL,(WRTPRT)	; GET STATUS BYTES.
	LD	A,(ACTIVE)	; WHICH DRIVE IS CURRENT?
	LD	C,A
	CALL	SHIFTR		; SHIFT STATUS SUCH THAT BIT 0 IS THE
	LD	A,L		; ONE OF INTEREST FOR THIS DRIVE.
	AND	01H		; AND ISOLATE IT.
	RET
;
;   FUNCTION TO WRITE PROTECT THE CURRENT DISK.
;
WRTPRTD:LD	HL,WRTPRT	; POINT TO STATUS WORD.
	LD	C,(HL)		; SET (BC) .EQUAL TO THE STATUS.
	INC	HL
	LD	B,(HL)
	CALL	SETBIT		; AND SET THIS BIT ACCORDING TO CURRENT DRIVE.
	LD	(WRTPRT),HL	; THEN SAVE.
	LD	HL,(DIRSIZE)	; NOW SAVE DIRECTORY SIZE LIMIT.
	INC	HL		; REMEMBER THE LAST ONE.
	EX	DE,HL
	LD	HL,(SCRATCH1)	; AND STORE IT HERE.
	LD	(HL),E		; PUT LOW BYTE.
	INC	HL
	LD	(HL),D		; THEN HIGH BYTE.
	RET
;
;   CHECK FOR A READ ONLY FILE.
;
CHKROFL:CALL	FCB2HL		; SET (HL) TO FILE ENTRY IN DIRECTORY BUFFER.
CKROF1:	LD	DE,9		; LOOK AT BIT 7 OF THE NINTH BYTE.
	ADD	HL,DE
	LD	A,(HL)
	RLA
	RET	NC		; RETURN IF OK.
	LD	HL,ROFILE	; ELSE, PRINT ERROR MESSAGE AND TERMINATE.
	JP	JUMPHL
;
;   CHECK THE WRITE PROTECT STATUS OF THE ACTIVE DISK.
;
CHKWPRT:CALL	GETWPRT
	RET	Z		; RETURN IF OK.
	LD	HL,RODISK	; ELSE PRINT MESSAGE AND TERMINATE.
	JP	JUMPHL
;
;   ROUTINE TO SET (HL) POINTING TO THE PROPER ENTRY IN THE
; DIRECTORY BUFFER.
;
FCB2HL:	LD	HL,(DIRBUF)	; GET ADDRESS OF BUFFER.
	LD	A,(FCBPOS)	; RELATIVE POSITION OF FILE.
;
;   ROUTINE TO ADD (A) TO (HL).
;
ADDA2HL:ADD	A,L
	LD	L,A
	RET	NC
	INC	H		; TAKE CARE OF ANY CARRY.
	RET
;
;   ROUTINE TO GET THE 'S2' BYTE FROM THE FCB SUPPLIED IN
; THE INITIAL PARAMETER SPECIFICATION.
;
GETS2:	LD	HL,(PARAMS)	; GET ADDRESS OF FCB.
	LD	DE,14		; RELATIVE POSITION OF 'S2'.
	ADD	HL,DE
	LD	A,(HL)		; EXTRACT THIS BYTE.
	RET
;
;   CLEAR THE 'S2' BYTE IN THE FCB.
;
CLEARS2:CALL	GETS2		; THIS SETS (HL) POINTING TO IT.
	LD	(HL),0		; NOW CLEAR IT.
	RET
;
;   SET BIT 7 IN THE 'S2' BYTE OF THE FCB.
;
SETS2B7:CALL	GETS2		; GET THE BYTE.
	OR	80H		; AND SET BIT 7.
	LD	(HL),A		; THEN STORE.
	RET
;
;   COMPARE (FILEPOS) WITH (SCRATCH1) AND SET FLAGS BASED ON
; THE DIFFERENCE. THIS CHECKS TO SEE IF THERE ARE MORE FILE
; NAMES IN THE DIRECTORY. WE ARE AT (FILEPOS) AND THERE ARE
; (SCRATCH1) OF THEM TO CHECK.
;
MOREFLS:LD	HL,(FILEPOS)	; WE ARE HERE.
	EX	DE,HL
	LD	HL,(SCRATCH1)	; AND DON'T GO PAST HERE.
	LD	A,E		; COMPUTE DIFFERENCE BUT DON'T KEEP.
	SUB	(HL)
	INC	HL
	LD	A,D
	SBC	A,(HL)		; SET CARRY IF NO MORE NAMES.
	RET
;
;   CALL THIS ROUTINE TO PREVENT (SCRATCH1) FROM BEING GREATER
; THAN (FILEPOS).
;
CHKNMBR:CALL	MOREFLS		; SCRATCH1 TOO BIG?
	RET	C
	INC	DE		; YES, RESET IT TO (FILEPOS).
	LD	(HL),D
	DEC	HL
	LD	(HL),E
	RET
;
;   COMPUTE (HL)=(DE)-(HL)
;
SUBHL:	LD	A,E		; COMPUTE DIFFERENCE.
	SUB	L
	LD	L,A		; STORE LOW BYTE.
	LD	A,D
	SBC	A,H
	LD	H,A		; AND THEN HIGH BYTE.
	RET
;
;   SET THE DIRECTORY CHECKSUM BYTE.
;
SETDIR:	LD	C,0FFH
;
;   ROUTINE TO SET OR COMPARE THE DIRECTORY CHECKSUM BYTE. IF
; (C)=0FFH, THEN THIS WILL SET THE CHECKSUM BYTE. ELSE THE BYTE
; WILL BE CHECKED. IF THE CHECK FAILS (THE DISK HAS BEEN CHANGED),
; THEN THIS DISK WILL BE WRITE PROTECTED.
;
CHECKDIR:
	LD	HL,(CKSUMTBL)
	EX	DE,HL
	LD	HL,(ALLOC1)
	CALL	SUBHL
	RET	NC		; OK IF (CKSUMTBL) > (ALLOC1), SO RETURN.
	PUSH	BC
	CALL	CHECKSUM	; ELSE COMPUTE CHECKSUM.
	LD	HL,(CHKVECT)	; GET ADDRESS OF CHECKSUM TABLE.
	EX	DE,HL
	LD	HL,(CKSUMTBL)
	ADD	HL,DE		; SET (HL) TO POINT TO BYTE FOR THIS DRIVE.
	POP	BC
	INC	C		; SET OR CHECK ?
	JP	Z,CHKDIR1
	CP	(HL)		; CHECK THEM.
	RET	Z		; RETURN IF THEY ARE THE SAME.
	CALL	MOREFLS		; NOT THE SAME, DO WE CARE?
	RET	NC
	CALL	WRTPRTD		; YES, MARK THIS AS WRITE PROTECTED.
	RET
CHKDIR1:LD	(HL),A		; JUST SET THE BYTE.
	RET
;
;   DO A WRITE TO THE DIRECTORY OF THE CURRENT DISK.
;
DIRWRITE:
	CALL	SETDIR		; SET CHECKSUM BYTE.
	CALL	DIRDMA		; SET DIRECTORY DMA ADDRESS.
	LD	C,1		; TELL THE BIOS TO ACTUALLY WRITE.
	CALL	DOWRITE		; THEN DO THE WRITE.
	JP	DEFDMA
;
;   READ FROM THE DIRECTORY.
;
DIRREAD:CALL	DIRDMA		; SET THE DIRECTORY DMA ADDRESS.
	CALL	DOREAD		; AND READ IT.
;
;   ROUTINE TO SET THE DMA ADDRESS TO THE USERS CHOICE.
;
DEFDMA:	LD	HL,USERDMA	; RESET THE DEFAULT DMA ADDRESS AND RETURN.
	JP	DIRDMA1
;
;   ROUTINE TO SET THE DMA ADDRESS FOR DIRECTORY WORK.
;
DIRDMA:	LD	HL,DIRBUF
;
;   SET THE DMA ADDRESS. ON ENTRY, (HL) POINTS TO
; WORD CONTAINING THE DESIRED DMA ADDRESS.
;
DIRDMA1:LD	C,(HL)
	INC	HL
	LD	B,(HL)		; SETUP (BC) AND GO TO THE BIOS TO SET IT.
	JP	SETDMA
;
;   MOVE THE DIRECTORY BUFFER INTO USER'S DMA SPACE.
;
MOVEDIR:LD	HL,(DIRBUF)	; BUFFER IS LOCATED HERE, AND
	EX	DE,HL
	LD	HL,(USERDMA)	; PUT IT HERE.
	LD	C,128		; THIS IS ITS LENGTH.
	JP	DE2HL		; MOVE IT NOW AND RETURN.
;
;   CHECK (FILEPOS) AND SET THE ZERO FLAG IF IT .EQUALS 0FFFFH.
;
CKFILPOS:
	LD	HL,FILEPOS
	LD	A,(HL)
	INC	HL
	CP	(HL)		; ARE BOTH BYTES THE SAME?
	RET	NZ
	INC	A		; YES, BUT ARE THEY EACH 0FFH?
	RET
;
;   SET LOCATION (FILEPOS) TO 0FFFFH.
;
STFILPOS:
	LD	HL,0FFFFH
	LD	(FILEPOS),HL
	RET
;
;   MOVE ON TO THE NEXT FILE POSITION WITHIN THE CURRENT
; DIRECTORY BUFFER. IF NO MORE EXIST, SET POINTER TO 0FFFFH
; AND THE CALLING ROUTINE WILL CHECK FOR THIS. ENTER WITH (C)
; .EQUAL TO 0FFH TO CAUSE THE CHECKSUM BYTE TO BE SET, ELSE WE
; WILL CHECK THIS DISK AND SET WRITE PROTECT IF CHECKSUMS ARE
; NOT THE SAME (APPLIES ONLY IF ANOTHER DIRECTORY SECTOR MUST
; BE READ).
;
NXENTRY:LD	HL,(DIRSIZE)	; GET DIRECTORY ENTRY SIZE LIMIT.
	EX	DE,HL
	LD	HL,(FILEPOS)	; GET CURRENT COUNT.
	INC	HL		; GO ON TO THE NEXT ONE.
	LD	(FILEPOS),HL
	CALL	SUBHL		; (HL)=(DIRSIZE)-(FILEPOS)
	JP	NC,NXENT1	; IS THERE MORE ROOM LEFT?
	JP	STFILPOS	; NO. SET THIS FLAG AND RETURN.
NXENT1:	LD	A,(FILEPOS)	; GET FILE POSITION WITHIN DIRECTORY.
	AND	03H		; ONLY LOOK WITHIN THIS SECTOR (ONLY 4 ENTRIES FIT).
	LD	B,5		; CONVERT TO RELATIVE POSITION (32 BYTES EACH).
NXENT2:	ADD	A,A		; NOTE THAT THIS IS NOT EFFICIENT CODE.
	DEC	B		; 5 'ADD A'S WOULD BE BETTER.
	JP	NZ,NXENT2
	LD	(FCBPOS),A	; SAVE IT AS POSITION OF FCB.
	OR	A
	RET	NZ		; RETURN IF WE ARE WITHIN BUFFER.
	PUSH	BC
	CALL	TRKSEC		; WE NEED THE NEXT DIRECTORY SECTOR.
	CALL	DIRREAD
	POP	BC
	JP	CHECKDIR
;
;   ROUTINE TO TO GET A BIT FROM THE DISK SPACE ALLOCATION
; MAP. IT IS RETURNED IN (A), BIT POSITION 0. ON ENTRY TO HERE,
; SET (BC) TO THE BLOCK NUMBER ON THE DISK TO CHECK.
; ON RETURN, (D) WILL CONTAIN THE ORIGINAL BIT POSITION FOR
; THIS BLOCK NUMBER AND (HL) WILL POINT TO THE ADDRESS FOR IT.
;
CKBITMAP:
	LD	A,C		; DETERMINE BIT NUMBER OF INTEREST.
	AND	07H		; COMPUTE (D)=(E)=(C AND 7)+1.
	INC	A
	LD	E,A		; SAVE PARTICULAR BIT NUMBER.
	LD	D,A
;
;   COMPUTE (BC)=(BC)/8.
;
	LD	A,C
	RRCA			; NOW SHIFT RIGHT 3 BITS.
	RRCA
	RRCA
	AND	1FH		; AND CLEAR BITS 7,6,5.
	LD	C,A
	LD	A,B
	ADD	A,A		; NOW SHIFT (B) INTO BITS 7,6,5.
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,A
	OR	C		; AND ADD IN (C).
	LD	C,A		; OK, (C) HA BEEN COMPLETED.
	LD	A,B		; IS THERE A BETTER WAY OF DOING THIS?
	RRCA
	RRCA
	RRCA
	AND	1FH
	LD	B,A		; AND NOW (B) IS COMPLETED.
;
;   USE THIS AS AN OFFSET INTO THE DISK SPACE ALLOCATION
; TABLE.
;
	LD	HL,(ALOCVECT)
	ADD	HL,BC
	LD	A,(HL)		; NOW GET CORRECT BYTE.
CKBMAP1:RLCA			; GET CORRECT BIT INTO POSITION 0.
	DEC	E
	JP	NZ,CKBMAP1
	RET
;
;   SET OR CLEAR THE BIT MAP SUCH THAT BLOCK NUMBER (BC) WILL BE MARKED
; AS USED. ON ENTRY, IF (E)=0 THEN THIS BIT WILL BE CLEARED, IF IT .EQUALS
; 1 THEN IT WILL BE SET (DON'T USE ANYOTHER VALUES).
;
STBITMAP:
	PUSH	DE
	CALL	CKBITMAP	; GET THE BYTE OF INTEREST.
	AND	0FEH		; CLEAR THE AFFECTED BIT.
	POP	BC
	OR	C		; AND NOW SET IT ACORDING TO (C).
;
;  ENTRY TO RESTORE THE ORIGINAL BIT POSITION AND THEN STORE
; IN TABLE. (A) CONTAINS THE VALUE, (D) CONTAINS THE BIT
; POSITION (1-8), AND (HL) POINTS TO THE ADDRESS WITHIN THE
; SPACE ALLOCATION TABLE FOR THIS BYTE.
;
STBMAP1:RRCA			; RESTORE ORIGINAL BIT POSITION.
	DEC	D
	JP	NZ,STBMAP1
	LD	(HL),A		; AND STOR BYTE IN TABLE.
	RET
;
;   SET/CLEAR SPACE USED BITS IN ALLOCATION MAP FOR THIS FILE.
; ON ENTRY, (C)=1 TO SET THE MAP AND (C)=0 TO CLEAR IT.
;
SETFILE:CALL	FCB2HL		; GET ADDRESS OF FCB
	LD	DE,16
	ADD	HL,DE		; GET TO BLOCK NUMBER BYTES.
	PUSH	BC
	LD	C,17		; CHECK ALL 17 BYTES (MAX) OF TABLE.
SETFL1:	POP	DE
	DEC	C		; DONE ALL BYTES YET?
	RET	Z
	PUSH	DE
	LD	A,(BIGDISK)	; CHECK DISK SIZE FOR 16 BIT BLOCK NUMBERS.
	OR	A
	JP	Z,SETFL2
	PUSH	BC		; ONLY 8 BIT NUMBERS. SET (BC) TO THIS ONE.
	PUSH	HL
	LD	C,(HL)		; GET LOW BYTE FROM TABLE, ALWAYS
	LD	B,0		; SET HIGH BYTE TO ZERO.
	JP	SETFL3
SETFL2:	DEC	C		; FOR 16 BIT BLOCK NUMBERS, ADJUST COUNTER.
	PUSH	BC
	LD	C,(HL)		; NOW GET BOTH THE LOW AND HIGH BYTES.
	INC	HL
	LD	B,(HL)
	PUSH	HL
SETFL3:	LD	A,C		; BLOCK USED?
	OR	B
	JP	Z,SETFL4
	LD	HL,(DSKSIZE)	; IS THIS BLOCK NUMBER WITHIN THE
	LD	A,L		; SPACE ON THE DISK?
	SUB	C
	LD	A,H
	SBC	A,B
	CALL	NC,STBITMAP	; YES, SET THE PROPER BIT.
SETFL4:	POP	HL		; POINT TO NEXT BLOCK NUMBER IN FCB.
	INC	HL
	POP	BC
	JP	SETFL1
;
;   CONSTRUCT THE SPACE USED ALLOCATION BIT MAP FOR THE ACTIVE
; DRIVE. IF A FILE NAME STARTS WITH '$' AND IT IS UNDER THE
; CURRENT USER NUMBER, THEN (STATUS) IS SET TO MINUS 1. OTHERWISE
; IT IS NOT SET AT ALL.
;
BITMAP:	LD	HL,(DSKSIZE)	; COMPUTE SIZE OF ALLOCATION TABLE.
	LD	C,3
	CALL	SHIFTR		; (HL)=(HL)/8.
	INC	HL		; AT LEASE 1 BYTE.
	LD	B,H
	LD	C,L		; SET (BC) TO THE ALLOCATION TABLE LENGTH.
;
;   INITIALIZE THE BITMAP FOR THIS DRIVE. RIGHT NOW, THE FIRST
; TWO BYTES ARE SPECIFIED BY THE DISK PARAMETER BLOCK. HOWEVER
; A PATCH COULD BE ENTERED HERE IF IT WERE NECESSARY TO SETUP
; THIS TABLE IN A SPECIAL MANNOR. FOR EXAMPLE, THE BIOS COULD
; DETERMINE LOCATIONS OF 'BAD BLOCKS' AND SET THEM AS ALREADY
; 'USED' IN THE MAP.
;
	LD	HL,(ALOCVECT)	; NOW ZERO OUT THE TABLE NOW.
BITMAP1:LD	(HL),0
	INC	HL
	DEC	BC
	LD	A,B
	OR	C
	JP	NZ,BITMAP1
	LD	HL,(ALLOC0)	; GET INITIAL SPACE USED BY DIRECTORY.
	EX	DE,HL
	LD	HL,(ALOCVECT)	; AND PUT THIS INTO MAP.
	LD	(HL),E
	INC	HL
	LD	(HL),D
;
;   END OF INITIALIZATION PORTION.
;
	CALL	HOMEDRV		; NOW HOME THE DRIVE.
	LD	HL,(SCRATCH1)
	LD	(HL),3		; FORCE NEXT DIRECTORY REQUEST TO READ
	INC	HL		; IN A SECTOR.
	LD	(HL),0
	CALL	STFILPOS	; CLEAR INITIAL FILE POSITION ALSO.
BITMAP2:LD	C,0FFH		; READ NEXT FILE NAME IN DIRECTORY
	CALL	NXENTRY		; AND SET CHECKSUM BYTE.
	CALL	CKFILPOS	; IS THERE ANOTHER FILE?
	RET	Z
	CALL	FCB2HL		; YES, GET ITS ADDRESS.
	LD	A,0E5H
	CP	(HL)		; EMPTY FILE ENTRY?
	JP	Z,BITMAP2
	LD	A,(USERNO)	; NO, CORRECT USER NUMBER?
	CP	(HL)
	JP	NZ,BITMAP3
	INC	HL
	LD	A,(HL)		; YES, DOES NAME START WITH A '$'?
	SUB	'$'
	JP	NZ,BITMAP3
	DEC	A		; YES, SET ATATUS TO MINUS ONE.
	LD	(STATUS),A
BITMAP3:LD	C,1		; NOW SET THIS FILE'S SPACE AS USED IN BIT MAP.
	CALL	SETFILE
	CALL	CHKNMBR		; KEEP (SCRATCH1) IN BOUNDS.
	JP	BITMAP2
;
;   SET THE STATUS (STATUS) AND RETURN.
;
STSTATUS:
	LD	A,(FNDSTAT)
	JP	SETSTAT
;
;   CHECK EXTENTS IN (A) AND (C). SET THE ZERO FLAG IF THEY
; ARE THE SAME. THE NUMBER OF 16K CHUNKS OF DISK SPACE THAT
; THE DIRECTORY EXTENT COVERS IS EXPRESSAD IS (EXTMASK+1).
; NO REGISTERS ARE MODIFIED.
;
SAMEXT:	PUSH	BC
	PUSH	AF
	LD	A,(EXTMASK)	; GET EXTENT MASK AND USE IT TO
	CPL			; TO COMPARE BOTH EXTENT NUMBERS.
	LD	B,A		; SAVE RESULTING MASK HERE.
	LD	A,C		; MASK FIRST EXTENT AND SAVE IN (C).
	AND	B
	LD	C,A
	POP	AF		; NOW MASK SECOND EXTENT AND COMPARE
	AND	B		; WITH THE FIRST ONE.
	SUB	C
	AND	1FH		; (* ONLY CHECK BUTS 0-4 *)
	POP	BC		; THE ZERO FLAG IS SET IF THEY ARE THE SAME.
	RET			; RESTORE (BC) AND RETURN.
;
;   SEARCH FOR THE FIRST OCCURENCE OF A FILE NAME. ON ENTRY,
; REGISTER (C) SHOULD CONTAIN THE NUMBER OF BYTES OF THE FCB
; THAT MUST MATCH.
;
FINDFST:LD	A,0FFH
	LD	(FNDSTAT),A
	LD	HL,COUNTER	; SAVE CHARACTER COUNT.
	LD	(HL),C
	LD	HL,(PARAMS)	; GET FILENAME TO MATCH.
	LD	(SAVEFCB),HL	; AND SAVE.
	CALL	STFILPOS	; CLEAR INITIAL FILE POSITION (SET TO 0FFFFH).
	CALL	HOMEDRV		; HOME THE DRIVE.
;
;   ENTRY TO LOCATE THE NEXT OCCURENCE OF A FILENAME WITHIN THE
; DIRECTORY. THE DISK IS NOT EXPECTED TO HAVE BEEN CHANGED. IF
; IT WAS, THEN IT WILL BE WRITE PROTECTED.
;
FINDNXT:LD	C,0		; WRITE PROTECT THE DISK IF CHANGED.
	CALL	NXENTRY		; GET NEXT FILENAME ENTRY IN DIRECTORY.
	CALL	CKFILPOS	; IS FILE POSITION = 0FFFFH?
	JP	Z,FNDNXT6	; YES, EXIT NOW THEN.
	LD	HL,(SAVEFCB)	; SET (DE) POINTING TO FILENAME TO MATCH.
	EX	DE,HL
	LD	A,(DE)
	CP	0E5H		; EMPTY DIRECTORY ENTRY?
	JP	Z,FNDNXT1	; (* ARE WE TRYING TO RESERECT ERASED ENTRIES? *)
	PUSH	DE
	CALL	MOREFLS		; MORE FILES IN DIRECTORY?
	POP	DE
	JP	NC,FNDNXT6	; NO MORE. EXIT NOW.
FNDNXT1:CALL	FCB2HL		; GET ADDRESS OF THIS FCB IN DIRECTORY.
	LD	A,(COUNTER)	; GET NUMBER OF BYTES (CHARACTERS) TO CHECK.
	LD	C,A
	LD	B,0		; INITIALIZE BYTE POSITION COUNTER.
FNDNXT2:LD	A,C		; ARE WE DONE WITH THE COMPARE?
	OR	A
	JP	Z,FNDNXT5
	LD	A,(DE)		; NO, CHECK NEXT BYTE.
	CP	'?'		; DON'T CARE ABOUT THIS CHARACTER?
	JP	Z,FNDNXT4
	LD	A,B		; GET BYTES POSITION IN FCB.
	CP	13		; DON'T CARE ABOUT THE THIRTEENTH BYTE EITHER.
	JP	Z,FNDNXT4
	CP	12		; EXTENT BYTE?
	LD	A,(DE)
	JP	Z,FNDNXT3
	SUB	(HL)		; OTHERWISE COMPARE CHARACTERS.
	AND	7FH
	JP	NZ,FINDNXT	; NOT THE SAME, CHECK NEXT ENTRY.
	JP	FNDNXT4		; SO FAR SO GOOD, KEEP CHECKING.
FNDNXT3:PUSH	BC		; CHECK THE EXTENT BYTE HERE.
	LD	C,(HL)
	CALL	SAMEXT
	POP	BC
	JP	NZ,FINDNXT	; NOT THE SAME, LOOK SOME MORE.
;
;   SO FAR THE NAMES COMPARE. BUMP POINTERS TO THE NEXT BYTE
; AND CONTINUE UNTIL ALL (C) CHARACTERS HAVE BEEN CHECKED.
;
FNDNXT4:INC	DE		; BUMP POINTERS.
	INC	HL
	INC	B
	DEC	C		; ADJUST CHARACTER COUNTER.
	JP	FNDNXT2
FNDNXT5:LD	A,(FILEPOS)	; RETURN THE POSITION OF THIS ENTRY.
	AND	03H
	LD	(STATUS),A
	LD	HL,FNDSTAT
	LD	A,(HL)
	RLA
	RET	NC
	XOR	A
	LD	(HL),A
	RET
;
;   FILENAME WAS NOT FOUND. SET APPROPRIATE STATUS.
;
FNDNXT6:CALL	STFILPOS	; SET (FILEPOS) TO 0FFFFH.
	LD	A,0FFH		; SAY NOT LOCATED.
	JP	SETSTAT
;
;   ERASE FILES FROM THE DIRECTORY. ONLY THE FIRST BYTE OF THE
; FCB WILL BE AFFECTED. IT IS SET TO (E5).
;
ERAFILE:CALL	CHKWPRT		; IS DISK WRITE PROTECTED?
	LD	C,12		; ONLY COMPARE FILE NAMES.
	CALL	FINDFST		; GET FIRST FILE NAME.
ERAFIL1:CALL	CKFILPOS	; ANY FOUND?
	RET	Z		; NOPE, WE MUST BE DONE.
	CALL	CHKROFL		; IS FILE READ ONLY?
	CALL	FCB2HL		; NOPE, GET ADDRESS OF FCB AND
	LD	(HL),0E5H	; SET FIRST BYTE TO 'EMPTY'.
	LD	C,0		; CLEAR THE SPACE FROM THE BIT MAP.
	CALL	SETFILE
	CALL	DIRWRITE	; NOW WRITE THE DIRECTORY SECTOR BACK OUT.
	CALL	FINDNXT		; FIND THE NEXT FILE NAME.
	JP	ERAFIL1		; AND REPEAT PROCESS.
;
;   LOOK THROUGH THE SPACE ALLOCATION MAP (BIT MAP) FOR THE
; NEXT AVAILABLE BLOCK. START SEARCHING AT BLOCK NUMBER (BC-1).
; THE SEARCH PROCEDURE IS TO LOOK FOR AN EMPTY BLOCK THAT IS
; BEFORE THE STARTING BLOCK. IF NOT EMPTY, LOOK AT A LATER
; BLOCK NUMBER. IN THIS WAY, WE RETURN THE CLOSEST EMPTY BLOCK
; ON EITHER SIDE OF THE 'TARGET' BLOCK NUMBER. THIS WILL SPEED
; ACCESS ON RANDOM DEVICES. FOR SERIAL DEVICES, THIS SHOULD BE
; CHANGED TO LOOK IN THE FORWARD DIRECTION FIRST AND THEN START
; AT THE FRONT AND SEARCH SOME MORE.
;
;   ON RETURN, (DE)= BLOCK NUMBER THAT IS EMPTY AND (HL) =0
; IF NO EMPRY BLOCK WAS FOUND.
;
FNDSPACE:
	LD	D,B		; SET (DE) AS THE BLOCK THAT IS CHECKED.
	LD	E,C
;
;   LOOK BEFORE TARGET BLOCK. REGISTERS (BC) ARE USED AS THE LOWER
; POINTER AND (DE) AS THE UPPER POINTER.
;
FNDSPA1:LD	A,C		; IS BLOCK 0 SPECIFIED?
	OR	B
	JP	Z,FNDSPA2
	DEC	BC		; NOPE, CHECK PREVIOUS BLOCK.
	PUSH	DE
	PUSH	BC
	CALL	CKBITMAP
	RRA			; IS THIS BLOCK EMPTY?
	JP	NC,FNDSPA3	; YES. USE THIS.
;
;   NOTE THAT THE ABOVE LOGIC GETS THE FIRST BLOCK THAT IT FINDS
; THAT IS EMPTY. THUS A FILE COULD BE WRITTEN 'BACKWARD' MAKING
; IT VERY SLOW TO ACCESS. THIS COULD BE CHANGED TO LOOK FOR THE
; FIRST EMPTY BLOCK AND THEN CONTINUE UNTIL THE START OF THIS
; EMPTY SPACE IS LOCATED AND THEN USED THAT STARTING BLOCK.
; THIS SHOULD HELP SPEED UP ACCESS TO SOME FILES ESPECIALLY ON
; A WELL USED DISK WITH LOTS OF FAIRLY SMALL 'HOLES'.
;
	POP	BC		; NOPE, CHECK SOME MORE.
	POP	DE
;
;   NOW LOOK AFTER TARGET BLOCK.
;
FNDSPA2:LD	HL,(DSKSIZE)	; IS BLOCK (DE) WITHIN DISK LIMITS?
	LD	A,E
	SUB	L
	LD	A,D
	SBC	A,H
	JP	NC,FNDSPA4
	INC	DE		; YES, MOVE ON TO NEXT ONE.
	PUSH	BC
	PUSH	DE
	LD	B,D
	LD	C,E
	CALL	CKBITMAP	; CHECK IT.
	RRA			; EMPTY?
	JP	NC,FNDSPA3
	POP	DE		; NOPE, CONTINUE SEARCHING.
	POP	BC
	JP	FNDSPA1
;
;   EMPTY BLOCK FOUND. SET IT AS USED AND RETURN WITH (HL)
; POINTING TO IT (TRUE?).
;
FNDSPA3:RLA			; RESET BYTE.
	INC	A		; AND SET BIT 0.
	CALL	STBMAP1		; UPDATE BIT MAP.
	POP	HL		; SET RETURN REGISTERS.
	POP	DE
	RET
;
;   FREE BLOCK WAS NOT FOUND. IF (BC) IS NOT ZERO, THEN WE HAVE
; NOT CHECKED ALL OF THE DISK SPACE.
;
FNDSPA4:LD	A,C
	OR	B
	JP	NZ,FNDSPA1
	LD	HL,0		; SET 'NOT FOUND' STATUS.
	RET
;
;   MOVE A COMPLETE FCB ENTRY INTO THE DIRECTORY AND WRITE IT.
;
FCBSET:	LD	C,0
	LD	E,32		; LENGTH OF EACH ENTRY.
;
;   MOVE (E) BYTES FROM THE FCB POINTED TO BY (PARAMS) INTO
; FCB IN DIRECTORY STARTING AT RELATIVE BYTE (C). THIS UPDATED
; DIRECTORY BUFFER IS THEN WRITTEN TO THE DISK.
;
UPDATE:	PUSH	DE
	LD	B,0		; SET (BC) TO RELATIVE BYTE POSITION.
	LD	HL,(PARAMS)	; GET ADDRESS OF FCB.
	ADD	HL,BC		; COMPUTE STARTING BYTE.
	EX	DE,HL
	CALL	FCB2HL		; GET ADDRESS OF FCB TO UPDATE IN DIRECTORY.
	POP	BC		; SET (C) TO NUMBER OF BYTES TO CHANGE.
	CALL	DE2HL
UPDATE1:CALL	TRKSEC		; DETERMINE THE TRACK AND SECTOR AFFECTED.
	JP	DIRWRITE	; THEN WRITE THIS SECTOR OUT.
;
;   ROUTINE TO CHANGE THE NAME OF ALL FILES ON THE DISK WITH A
; SPECIFIED NAME. THE FCB CONTAINS THE CURRENT NAME AS THE
; FIRST 12 CHARACTERS AND THE NEW NAME 16 BYTES INTO THE FCB.
;
CHGNAMES:
	CALL	CHKWPRT		; CHECK FOR A WRITE PROTECTED DISK.
	LD	C,12		; MATCH FIRST 12 BYTES OF FCB ONLY.
	CALL	FINDFST		; GET FIRST NAME.
	LD	HL,(PARAMS)	; GET ADDRESS OF FCB.
	LD	A,(HL)		; GET USER NUMBER.
	LD	DE,16		; MOVE OVER TO DESIRED NAME.
	ADD	HL,DE
	LD	(HL),A		; KEEP SAME USER NUMBER.
CHGNAM1:CALL	CKFILPOS	; ANY MATCHING FILE FOUND?
	RET	Z		; NO, WE MUST BE DONE.
	CALL	CHKROFL		; CHECK FOR READ ONLY FILE.
	LD	C,16		; START 16 BYTES INTO FCB.
	LD	E,12		; AND UPDATE THE FIRST 12 BYTES OF DIRECTORY.
	CALL	UPDATE
	CALL	FINDNXT		; GET TE NEXT FILE NAME.
	JP	CHGNAM1		; AND CONTINUE.
;
;   UPDATE A FILES ATTRIBUTES. THE PROCEDURE IS TO SEARCH FOR
; EVERY FILE WITH THE SAME NAME AS SHOWN IN FCB (IGNORING BIT 7)
; AND THEN TO UPDATE IT (WHICH INCLUDES BIT 7). NO OTHER CHANGES
; ARE MADE.
;
SAVEATTR:
	LD	C,12		; MATCH FIRST 12 BYTES.
	CALL	FINDFST		; LOOK FOR FIRST FILENAME.
SAVATR1:CALL	CKFILPOS	; WAS ONE FOUND?
	RET	Z		; NOPE, WE MUST BE DONE.
	LD	C,0		; YES, UPDATE THE FIRST 12 BYTES NOW.
	LD	E,12
	CALL	UPDATE		; UPDATE FILENAME AND WRITE DIRECTORY.
	CALL	FINDNXT		; AND GET THE NEXT FILE.
	JP	SAVATR1		; THEN CONTINUE UNTIL DONE.
;
;  OPEN A FILE (NAME SPECIFIED IN FCB).
;
OPENIT:	LD	C,15		; COMPARE THE FIRST 15 BYTES.
	CALL	FINDFST		; GET THE FIRST ONE IN DIRECTORY.
	CALL	CKFILPOS	; ANY AT ALL?
	RET	Z
OPENIT1:CALL	SETEXT		; POINT TO EXTENT BYTE WITHIN USERS FCB.
	LD	A,(HL)		; AND GET IT.
	PUSH	AF		; SAVE IT AND ADDRESS.
	PUSH	HL
	CALL	FCB2HL		; POINT TO FCB IN DIRECTORY.
	EX	DE,HL
	LD	HL,(PARAMS)	; THIS IS THE USERS COPY.
	LD	C,32		; MOVE IT INTO USERS SPACE.
	PUSH	DE
	CALL	DE2HL
	CALL	SETS2B7		; SET BIT 7 IN 'S2' BYTE (UNMODIFIED).
	POP	DE		; NOW GET THE EXTENT BYTE FROM THIS FCB.
	LD	HL,12
	ADD	HL,DE
	LD	C,(HL)		; INTO (C).
	LD	HL,15		; NOW GET THE RECORD COUNT BYTE INTO (B).
	ADD	HL,DE
	LD	B,(HL)
	POP	HL		; KEEP THE SAME EXTENT AS THE USER HAD ORIGINALLY.
	POP	AF
	LD	(HL),A
	LD	A,C		; IS IT THE SAME AS IN THE DIRECTORY FCB?
	CP	(HL)
	LD	A,B		; IF YES, THEN USE THE SAME RECORD COUNT.
	JP	Z,OPENIT2
	LD	A,0		; IF THE USER SPECIFIED AN EXTENT GREATER THAN
	JP	C,OPENIT2	; THE ONE IN THE DIRECTORY, THEN SET RECORD COUNT TO 0.
	LD	A,128		; OTHERWISE SET TO MAXIMUM.
OPENIT2:LD	HL,(PARAMS)	; SET RECORD COUNT IN USERS FCB TO (A).
	LD	DE,15
	ADD	HL,DE		; COMPUTE RELATIVE POSITION.
	LD	(HL),A		; AND SET THE RECORD COUNT.
	RET
;
;   MOVE TWO BYTES FROM (DE) TO (HL) IF (AND ONLY IF) (HL)
; POINT TO A ZERO VALUE (16 BIT).
;   RETURN WITH ZERO FLAG SET IT (DE) WAS MOVED. REGISTERS (DE)
; AND (HL) ARE NOT CHANGED. HOWEVER (A) IS.
;
MOVEWORD:
	LD	A,(HL)		; CHECK FOR A ZERO WORD.
	INC	HL
	OR	(HL)		; BOTH BYTES ZERO?
	DEC	HL
	RET	NZ		; NOPE, JUST RETURN.
	LD	A,(DE)		; YES, MOVE TWO BYTES FROM (DE) INTO
	LD	(HL),A		; THIS ZERO SPACE.
	INC	DE
	INC	HL
	LD	A,(DE)
	LD	(HL),A
	DEC	DE		; DON'T DISTURB THESE REGISTERS.
	DEC	HL
	RET
;
;   GET HERE TO CLOSE A FILE SPECIFIED BY (FCB).
;
CLOSEIT:XOR	A		; CLEAR STATUS AND FILE POSITION BYTES.
	LD	(STATUS),A
	LD	(FILEPOS),A
	LD	(FILEPOS+1),A
	CALL	GETWPRT		; GET WRITE PROTECT BIT FOR THIS DRIVE.
	RET	NZ		; JUST RETURN IF IT IS SET.
	CALL	GETS2		; ELSE GET THE 'S2' BYTE.
	AND	80H		; AND LOOK AT BIT 7 (FILE UNMODIFIED?).
	RET	NZ		; JUST RETURN IF SET.
	LD	C,15		; ELSE LOOK UP THIS FILE IN DIRECTORY.
	CALL	FINDFST
	CALL	CKFILPOS	; WAS IT FOUND?
	RET	Z		; JUST RETURN IF NOT.
	LD	BC,16		; SET (HL) POINTING TO RECORDS USED SECTION.
	CALL	FCB2HL
	ADD	HL,BC
	EX	DE,HL
	LD	HL,(PARAMS)	; DO THE SAME FOR USERS SPECIFIED FCB.
	ADD	HL,BC
	LD	C,16		; THIS MANY BYTES ARE PRESENT IN THIS EXTENT.
CLOSEIT1:
	LD	A,(BIGDISK)	; 8 OR 16 BIT RECORD NUMBERS?
	OR	A
	JP	Z,CLOSEIT4
	LD	A,(HL)		; JUST 8 BIT. GET ONE FROM USERS FCB.
	OR	A
	LD	A,(DE)		; NOW GET ONE FROM DIRECTORY FCB.
	JP	NZ,CLOSEIT2
	LD	(HL),A		; USERS BYTE WAS ZERO. UPDATE FROM DIRECTORY.
CLOSEIT2:
	OR	A
	JP	NZ,CLOSEIT3
	LD	A,(HL)		; DIRECTORIES BYTE WAS ZERO, UPDATE FROM USERS FCB.
	LD	(DE),A
CLOSEIT3:
	CP	(HL)		; IF NEITHER ONE OF THESE BYTES WERE ZERO,
	JP	NZ,CLOSEIT7	; THEN CLOSE ERROR IF THEY ARE NOT THE SAME.
	JP	CLOSEIT5	; OK SO FAR, GET TO NEXT BYTE IN FCBS.
CLOSEIT4:
	CALL	MOVEWORD	; UPDATE USERS FCB IF IT IS ZERO.
	EX	DE,HL
	CALL	MOVEWORD	; UPDATE DIRECTORIES FCB IF IT IS ZERO.
	EX	DE,HL
	LD	A,(DE)		; IF THESE TWO VALUES ARE NO DIFFERENT,
	CP	(HL)		; THEN A CLOSE ERROR OCCURED.
	JP	NZ,CLOSEIT7
	INC	DE		; CHECK SECOND BYTE.
	INC	HL
	LD	A,(DE)
	CP	(HL)
	JP	NZ,CLOSEIT7
	DEC	C		; REMEMBER 16 BIT VALUES.
CLOSEIT5:
	INC	DE		; BUMP TO NEXT ITEM IN TABLE.
	INC	HL
	DEC	C		; THERE ARE 16 ENTRIES ONLY.
	JP	NZ,CLOSEIT1	; CONTINUE IF MORE TO DO.
	LD	BC,0FFECH	; BACKUP 20 PLACES (EXTENT BYTE).
	ADD	HL,BC
	EX	DE,HL
	ADD	HL,BC
	LD	A,(DE)
	CP	(HL)		; DIRECTORY'S EXTENT ALREADY GREATER THAN THE
	JP	C,CLOSEIT6	; USERS EXTENT?
	LD	(HL),A		; NO, UPDATE DIRECTORY EXTENT.
	LD	BC,3		; AND UPDATE THE RECORD COUNT BYTE IN
	ADD	HL,BC		; DIRECTORIES FCB.
	EX	DE,HL
	ADD	HL,BC
	LD	A,(HL)		; GET FROM USER.
	LD	(DE),A		; AND PUT IN DIRECTORY.
CLOSEIT6:
	LD	A,0FFH		; SET 'WAS OPEN AND IS NOW CLOSED' BYTE.
	LD	(CLOSEFLG),A
	JP	UPDATE1		; UPDATE THE DIRECTORY NOW.
CLOSEIT7:
	LD	HL,STATUS	; SET RETURN STATUS AND THEN RETURN.
	DEC	(HL)
	RET
;
;   ROUTINE TO GET THE NEXT EMPTY SPACE IN THE DIRECTORY. IT
; WILL THEN BE CLEARED FOR USE.
;
GETEMPTY:
	CALL	CHKWPRT		; MAKE SURE DISK IS NOT WRITE PROTECTED.
	LD	HL,(PARAMS)	; SAVE CURRENT PARAMETERS (FCB).
	PUSH	HL
	LD	HL,EMPTYFCB	; USE SPECIAL ONE FOR EMPTY SPACE.
	LD	(PARAMS),HL
	LD	C,1		; SEARCH FOR FIRST EMPTY SPOT IN DIRECTORY.
	CALL	FINDFST		; (* ONLY CHECK FIRST BYTE *)
	CALL	CKFILPOS	; NONE?
	POP	HL
	LD	(PARAMS),HL	; RESTORE ORIGINAL FCB ADDRESS.
	RET	Z		; RETURN IF NO MORE SPACE.
	EX	DE,HL
	LD	HL,15		; POINT TO NUMBER OF RECORDS FOR THIS FILE.
	ADD	HL,DE
	LD	C,17		; AND CLEAR ALL OF THIS SPACE.
	XOR	A
GETMT1:	LD	(HL),A
	INC	HL
	DEC	C
	JP	NZ,GETMT1
	LD	HL,13		; CLEAR THE 'S1' BYTE ALSO.
	ADD	HL,DE
	LD	(HL),A
	CALL	CHKNMBR		; KEEP (SCRATCH1) WITHIN BOUNDS.
	CALL	FCBSET		; WRITE OUT THIS FCB ENTRY TO DIRECTORY.
	JP	SETS2B7		; SET 'S2' BYTE BIT 7 (UNMODIFIED AT PRESENT).
;
;   ROUTINE TO CLOSE THE CURRENT EXTENT AND OPEN THE NEXT ONE
; FOR READING.
;
GETNEXT:XOR	A
	LD	(CLOSEFLG),A	; CLEAR CLOSE FLAG.
	CALL	CLOSEIT		; CLOSE THIS EXTENT.
	CALL	CKFILPOS
	RET	Z		; NOT THERE???
	LD	HL,(PARAMS)	; GET EXTENT BYTE.
	LD	BC,12
	ADD	HL,BC
	LD	A,(HL)		; AND INCREMENT IT.
	INC	A
	AND	1FH		; KEEP WITHIN RANGE 0-31.
	LD	(HL),A
	JP	Z,GTNEXT1	; OVERFLOW?
	LD	B,A		; MASK EXTENT BYTE.
	LD	A,(EXTMASK)
	AND	B
	LD	HL,CLOSEFLG	; CHECK CLOSE FLAG (0FFH IS OK).
	AND	(HL)
	JP	Z,GTNEXT2	; IF ZERO, WE MUST READ IN NEXT EXTENT.
	JP	GTNEXT3		; ELSE, IT IS ALREADY IN MEMORY.
GTNEXT1:LD	BC,2		; POINT TO THE 'S2' BYTE.
	ADD	HL,BC
	INC	(HL)		; AND BUMP IT.
	LD	A,(HL)		; TOO MANY EXTENTS?
	AND	0FH
	JP	Z,GTNEXT5	; YES, SET ERROR CODE.
;
;   GET HERE TO OPEN THE NEXT EXTENT.
;
GTNEXT2:LD	C,15		; SET TO CHECK FIRST 15 BYTES OF FCB.
	CALL	FINDFST		; FIND THE FIRST ONE.
	CALL	CKFILPOS	; NONE AVAILABLE?
	JP	NZ,GTNEXT3
	LD	A,(RDWRTFLG)	; NO EXTENT PRESENT. CAN WE OPEN AN EMPTY ONE?
	INC	A		; 0FFH MEANS READING (SO NOT POSSIBLE).
	JP	Z,GTNEXT5	; OR AN ERROR.
	CALL	GETEMPTY	; WE ARE WRITING, GET AN EMPTY ENTRY.
	CALL	CKFILPOS	; NONE?
	JP	Z,GTNEXT5	; ERROR IF TRUE.
	JP	GTNEXT4		; ELSE WE ARE ALMOST DONE.
GTNEXT3:CALL	OPENIT1		; OPEN THIS EXTENT.
GTNEXT4:CALL	STRDATA		; MOVE IN UPDATED DATA (REC #, EXTENT #, ETC.)
	XOR	A		; CLEAR STATUS AND RETURN.
	JP	SETSTAT
;
;   ERROR IN EXTENDING THE FILE. TOO MANY EXTENTS WERE NEEDED
; OR NOT ENOUGH SPACE ON THE DISK.
;
GTNEXT5:CALL	IOERR1		; SET ERROR CODE, CLEAR BIT 7 OF 'S2'
	JP	SETS2B7		; SO THIS IS NOT WRITTEN ON A CLOSE.
;
;   READ A S.EQUENTIAL FILE.
;
RDSEQ:	LD	A,1		; SET S.EQUENTIAL ACCESS MODE.
	LD	(MODE),A
RDSEQ1:	LD	A,0FFH		; DON'T ALLOW READING UNWRITTEN SPACE.
	LD	(RDWRTFLG),A
	CALL	STRDATA		; PUT REC# AND EXT# INTO FCB.
	LD	A,(SAVNREC)	; GET NEXT RECORD TO READ.
	LD	HL,SAVNXT	; GET NUMBER OF RECORDS IN EXTENT.
	CP	(HL)		; WITHIN THIS EXTENT?
	JP	C,RDSEQ2
	CP	128		; NO. IS THIS EXTENT FULLY USED?
	JP	NZ,RDSEQ3	; NO. END-OF-FILE.
	CALL	GETNEXT		; YES, OPEN THE NEXT ONE.
	XOR	A		; RESET NEXT RECORD TO READ.
	LD	(SAVNREC),A
	LD	A,(STATUS)	; CHECK ON OPEN, SUCCESSFUL?
	OR	A
	JP	NZ,RDSEQ3	; NO, ERROR.
RDSEQ2:	CALL	COMBLK		; OK. COMPUTE BLOCK NUMBER TO READ.
	CALL	CHKBLK		; CHECK IT. WITHIN BOUNDS?
	JP	Z,RDSEQ3	; NO, ERROR.
	CALL	LOGICAL		; CONVERT (BLKNMBR) TO LOGICAL SECTOR (128 BYTE).
	CALL	TRKSEC1		; SET THE TRACK AND SECTOR FOR THIS BLOCK #.
	CALL	DOREAD		; AND READ IT.
	JP	SETNREC		; AND SET THE NEXT RECORD TO BE ACCESSED.
;
;   READ ERROR OCCURED. SET STATUS AND RETURN.
;
RDSEQ3:	JP	IOERR1
;
;   WRITE THE NEXT S.EQUENTIAL RECORD.
;
WTSEQ:	LD	A,1		; SET S.EQUENTIAL ACCESS MODE.
	LD	(MODE),A
WTSEQ1:	LD	A,0		; ALLOW AN ADDITION EMPTY EXTENT TO BE OPENED.
	LD	(RDWRTFLG),A
	CALL	CHKWPRT		; CHECK WRITE PROTECT STATUS.
	LD	HL,(PARAMS)
	CALL	CKROF1		; CHECK FOR READ ONLY FILE, (HL) ALREADY SET TO FCB.
	CALL	STRDATA		; PUT UPDATED DATA INTO FCB.
	LD	A,(SAVNREC)	; GET RECORD NUMBER TO WRITE.
	CP	128		; WITHIN RANGE?
	JP	NC,IOERR1	; NO, ERROR(?).
	CALL	COMBLK		; COMPUTE BLOCK NUMBER.
	CALL	CHKBLK		; CHECK NUMBER.
	LD	C,0		; IS THERE ONE TO WRITE TO?
	JP	NZ,WTSEQ6	; YES, GO DO IT.
	CALL	GETBLOCK	; GET NEXT BLOCK NUMBER WITHIN FCB TO USE.
	LD	(RELBLOCK),A	; AND SAVE.
	LD	BC,0		; START LOOKING FOR SPACE FROM THE START
	OR	A		; IF NONE ALLOCATED AS YET.
	JP	Z,WTSEQ2
	LD	C,A		; EXTRACT PREVIOUS BLOCK NUMBER FROM FCB
	DEC	BC		; SO WE CAN BE CLOSEST TO IT.
	CALL	EXTBLK
	LD	B,H
	LD	C,L
WTSEQ2:	CALL	FNDSPACE	; FIND THE NEXT EMPTY BLOCK NEAREST NUMBER (BC).
	LD	A,L		; CHECK FOR A ZERO NUMBER.
	OR	H
	JP	NZ,WTSEQ3
	LD	A,2		; NO MORE SPACE?
	JP	SETSTAT
WTSEQ3:	LD	(BLKNMBR),HL	; SAVE BLOCK NUMBER TO ACCESS.
	EX	DE,HL		; PUT BLOCK NUMBER INTO (DE).
	LD	HL,(PARAMS)	; NOW WE MUST UPDATE THE FCB FOR THIS
	LD	BC,16		; NEWLY ALLOCATED BLOCK.
	ADD	HL,BC
	LD	A,(BIGDISK)	; 8 OR 16 BIT BLOCK NUMBERS?
	OR	A
	LD	A,(RELBLOCK)	; (* UPDATE THIS ENTRY *)
	JP	Z,WTSEQ4	; ZERO MEANS 16 BIT ONES.
	CALL	ADDA2HL		; (HL)=(HL)+(A)
	LD	(HL),E		; STORE NEW BLOCK NUMBER.
	JP	WTSEQ5
WTSEQ4:	LD	C,A		; COMPUTE SPOT IN THIS 16 BIT TABLE.
	LD	B,0
	ADD	HL,BC
	ADD	HL,BC
	LD	(HL),E		; STUFF BLOCK NUMBER (DE) THERE.
	INC	HL
	LD	(HL),D
WTSEQ5:	LD	C,2		; SET (C) TO INDICATE WRITING TO UN-USED DISK SPACE.
WTSEQ6:	LD	A,(STATUS)	; ARE WE OK SO FAR?
	OR	A
	RET	NZ
	PUSH	BC		; YES, SAVE WRITE FLAG FOR BIOS (REGISTER C).
	CALL	LOGICAL		; CONVERT (BLKNMBR) OVER TO LOICAL SECTORS.
	LD	A,(MODE)	; GET ACCESS MODE FLAG (1=S.EQUENTIAL,
	DEC	A		; 0=RANDOM, 2=SPECIAL?).
	DEC	A
	JP	NZ,WTSEQ9
;
;   SPECIAL RANDOM I/O FROM FUNCTION #40. MAYBE FOR M/PM, BUT THE
; CURRENT BLOCK, IF IT HAS NOT BEEN WRITTEN TO, WILL BE ZEROED
; OUT AND THEN WRITTEN (REASON?).
;
	POP	BC
	PUSH	BC
	LD	A,C		; GET WRITE STATUS FLAG (2=WRITING UNUSED SPACE).
	DEC	A
	DEC	A
	JP	NZ,WTSEQ9
	PUSH	HL
	LD	HL,(DIRBUF)	; ZERO OUT THE DIRECTORY BUFFER.
	LD	D,A		; NOTE THAT (A) IS ZERO HERE.
WTSEQ7:	LD	(HL),A
	INC	HL
	INC	D		; DO 128 BYTES.
	JP	P,WTSEQ7
	CALL	DIRDMA		; TELL THE BIOS THE DMA ADDRESS FOR DIRECTORY ACCESS.
	LD	HL,(LOGSECT)	; GET SECTOR THAT STARTS CURRENT BLOCK.
	LD	C,2		; SET 'WRITING TO UNUSED SPACE' FLAG.
WTSEQ8:	LD	(BLKNMBR),HL	; SAVE SECTOR TO WRITE.
	PUSH	BC
	CALL	TRKSEC1		; DETERMINE ITS TRACK AND SECTOR NUMBERS.
	POP	BC
	CALL	DOWRITE		; NOW WRITE OUT 128 BYTES OF ZEROS.
	LD	HL,(BLKNMBR)	; GET SECTOR NUMBER.
	LD	C,0		; SET NORMAL WRITE FLAG.
	LD	A,(BLKMASK)	; DETERMINE IF WE HAVE WRITTEN THE ENTIRE
	LD	B,A		; PHYSICAL BLOCK.
	AND	L
	CP	B
	INC	HL		; PREPARE FOR THE NEXT ONE.
	JP	NZ,WTSEQ8	; CONTINUE UNTIL (BLKMASK+1) SECTORS WRITTEN.
	POP	HL		; RESET NEXT SECTOR NUMBER.
	LD	(BLKNMBR),HL
	CALL	DEFDMA		; AND RESET DMA ADDRESS.
;
;   NORMAL DISK WRITE. SET THE DESIRED TRACK AND SECTOR THEN
; DO THE ACTUAL WRITE.
;
WTSEQ9:	CALL	TRKSEC1		; DETERMINE TRACK AND SECTOR FOR THIS WRITE.
	POP	BC		; GET WRITE STATUS FLAG.
	PUSH	BC
	CALL	DOWRITE		; AND WRITE THIS OUT.
	POP	BC
	LD	A,(SAVNREC)	; GET NUMBER OF RECORDS IN FILE.
	LD	HL,SAVNXT	; GET LAST RECORD WRITTEN.
	CP	(HL)
	JP	C,WTSEQ10
	LD	(HL),A		; WE HAVE TO UPDATE RECORD COUNT.
	INC	(HL)
	LD	C,2
;
;*   THIS AREA HAS BEEN PATCHED TO CORRECT DISK UPDATE PROBLEM
;* WHEN USING BLOCKING AND DE-BLOCKING IN THE BIOS.
;
WTSEQ10:NOP			; WAS 'DCR C'
	NOP			; WAS 'DCR C'
	LD	HL,0		; WAS 'JNZ WTSEQ99'
;
; *   END OF PATCH.
;
	PUSH	AF
	CALL	GETS2		; SET 'EXTENT WRITTEN TO' FLAG.
	AND	7FH		; (* CLEAR BIT 7 *)
	LD	(HL),A
	POP	AF		; GET RECORD COUNT FOR THIS EXTENT.
WTSEQ99:CP	127		; IS IT FULL?
	JP	NZ,WTSEQ12
	LD	A,(MODE)	; YES, ARE WE IN S.EQUENTIAL MODE?
	CP	1
	JP	NZ,WTSEQ12
	CALL	SETNREC		; YES, SET NEXT RECORD NUMBER.
	CALL	GETNEXT		; AND GET NEXT EMPTY SPACE IN DIRECTORY.
	LD	HL,STATUS	; OK?
	LD	A,(HL)
	OR	A
	JP	NZ,WTSEQ11
	DEC	A		; YES, SET RECORD COUNT TO -1.
	LD	(SAVNREC),A
WTSEQ11:LD	(HL),0		; CLEAR STATUS.
WTSEQ12:JP	SETNREC		; SET NEXT RECORD TO ACCESS.
;
;   FOR RANDOM I/O, SET THE FCB FOR THE DESIRED RECORD NUMBER
; BASED ON THE 'R0,R1,R2' BYTES. THESE BYTES IN THE FCB ARE
; USED AS FOLLOWS:
;
;       FCB+35            FCB+34            FCB+33
;  |     'R-2'      |      'R-1'      |      'R-0'     |
;  |7             0 | 7             0 | 7             0|
;  |0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0|
;  |    OVERFLOW   | | EXTRA |  EXTENT   |   RECORD #  |
;  | ______________| |_EXTENT|__NUMBER___|_____________|
;                     ALSO 'S2'
;
;   ON ENTRY, REGISTER (C) CONTAINS 0FFH IF THIS IS A READ
; AND THUS WE CAN NOT ACCESS UNWRITTEN DISK SPACE. OTHERWISE,
; ANOTHER EXTENT WILL BE OPENED (FOR WRITING) IF REQUIRED.
;
POSITION:
	XOR	A		; SET RANDOM I/O FLAG.
	LD	(MODE),A
;
;   SPECIAL ENTRY (FUNCTION #40). M/PM ?
;
POSITN1:PUSH	BC		; SAVE READ/WRITE FLAG.
	LD	HL,(PARAMS)	; GET ADDRESS OF FCB.
	EX	DE,HL
	LD	HL,33		; NOW GET BYTE 'R0'.
	ADD	HL,DE
	LD	A,(HL)
	AND	7FH		; KEEP BITS 0-6 FOR THE RECORD NUMBER TO ACCESS.
	PUSH	AF
	LD	A,(HL)		; NOW GET BIT 7 OF 'R0' AND BITS 0-3 OF 'R1'.
	RLA
	INC	HL
	LD	A,(HL)
	RLA
	AND	1FH		; AND SAVE THIS IN BITS 0-4 OF (C).
	LD	C,A		; THIS IS THE EXTENT BYTE.
	LD	A,(HL)		; NOW GET THE EXTRA EXTENT BYTE.
	RRA
	RRA
	RRA
	RRA
	AND	0FH
	LD	B,A		; AND SAVE IT IN (B).
	POP	AF		; GET RECORD NUMBER BACK TO (A).
	INC	HL		; CHECK OVERFLOW BYTE 'R2'.
	LD	L,(HL)
	INC	L
	DEC	L
	LD	L,6		; PREPARE FOR ERROR.
	JP	NZ,POSITN5	; OUT OF DISK SPACE ERROR.
	LD	HL,32		; STORE RECORD NUMBER INTO FCB.
	ADD	HL,DE
	LD	(HL),A
	LD	HL,12		; AND NOW CHECK THE EXTENT BYTE.
	ADD	HL,DE
	LD	A,C
	SUB	(HL)		; SAME EXTENT AS BEFORE?
	JP	NZ,POSITN2
	LD	HL,14		; YES, CHECK EXTRA EXTENT BYTE 'S2' ALSO.
	ADD	HL,DE
	LD	A,B
	SUB	(HL)
	AND	7FH
	JP	Z,POSITN3	; SAME, WE ARE ALMOST DONE THEN.
;
;  GET HERE WHEN ANOTHER EXTENT IS REQUIRED.
;
POSITN2:PUSH	BC
	PUSH	DE
	CALL	CLOSEIT		; CLOSE CURRENT EXTENT.
	POP	DE
	POP	BC
	LD	L,3		; PREPARE FOR ERROR.
	LD	A,(STATUS)
	INC	A
	JP	Z,POSITN4	; CLOSE ERROR.
	LD	HL,12		; PUT DESIRED EXTENT INTO FCB NOW.
	ADD	HL,DE
	LD	(HL),C
	LD	HL,14		; AND STORE EXTRA EXTENT BYTE 'S2'.
	ADD	HL,DE
	LD	(HL),B
	CALL	OPENIT		; TRY AND GET THIS EXTENT.
	LD	A,(STATUS)	; WAS IT THERE?
	INC	A
	JP	NZ,POSITN3
	POP	BC		; NO. CAN WE CREATE A NEW ONE (WRITING?).
	PUSH	BC
	LD	L,4		; PREPARE FOR ERROR.
	INC	C
	JP	Z,POSITN4	; NOPE, READING UNWRITTEN SPACE ERROR.
	CALL	GETEMPTY	; YES WE CAN, TRY TO FIND SPACE.
	LD	L,5		; PREPARE FOR ERROR.
	LD	A,(STATUS)
	INC	A
	JP	Z,POSITN4	; OUT OF SPACE?
;
;   NORMAL RETURN LOCATION. CLEAR ERROR CODE AND RETURN.
;
POSITN3:POP	BC		; RESTORE STACK.
	XOR	A		; AND CLEAR ERROR CODE BYTE.
	JP	SETSTAT
;
;   ERROR. SET THE 'S2' BYTE TO INDICATE THIS (WHY?).
;
POSITN4:PUSH	HL
	CALL	GETS2
	LD	(HL),0C0H
	POP	HL
;
;   RETURN WITH ERROR CODE (PRESENTLY IN L).
;
POSITN5:POP	BC
	LD	A,L		; GET ERROR CODE.
	LD	(STATUS),A
	JP	SETS2B7
;
;   READ A RANDOM RECORD.
;
READRAN:LD	C,0FFH		; SET 'READ' STATUS.
	CALL	POSITION	; POSITION THE FILE TO PROPER RECORD.
	CALL	Z,RDSEQ1	; AND READ IT AS USUAL (IF NO ERRORS).
	RET
;
;   WRITE TO A RANDOM RECORD.
;
WRITERAN:
	LD	C,0		; SET 'WRITING' FLAG.
	CALL	POSITION	; POSITION THE FILE TO PROPER RECORD.
	CALL	Z,WTSEQ1	; AND WRITE AS USUAL (IF NO ERRORS).
	RET
;
;   COMPUTE THE RANDOM RECORD NUMBER. ENTER WITH (HL) POINTING
; TO A FCB AN (DE) CONTAINS A RELATIVE LOCATION OF A RECORD
; NUMBER. ON EXIT, (C) CONTAINS THE 'R0' BYTE, (B) THE 'R1'
; BYTE, AND (A) THE 'R2' BYTE.
;
;   ON RETURN, THE ZERO FLAG IS SET IF THE RECORD IS WITHIN
; BOUNDS. OTHERWISE, AN OVERFLOW OCCURED.
;
COMPRAND:
	EX	DE,HL		; SAVE FCB POINTER IN (DE).
	ADD	HL,DE		; COMPUTE RELATIVE POSITION OF RECORD #.
	LD	C,(HL)		; GET RECORD NUMBER INTO (BC).
	LD	B,0
	LD	HL,12		; NOW GET EXTENT.
	ADD	HL,DE
	LD	A,(HL)		; COMPUTE (BC)=(RECORD #)+(EXTENT)*128.
	RRCA			; MOVE LOWER BIT INTO BIT 7.
	AND	80H		; AND IGNORE ALL OTHER BITS.
	ADD	A,C		; ADD TO OUR RECORD NUMBER.
	LD	C,A
	LD	A,0		; TAKE CARE OF ANY CARRY.
	ADC	A,B
	LD	B,A
	LD	A,(HL)		; NOW GET THE UPPER BITS OF EXTENT INTO
	RRCA			; BIT POSITIONS 0-3.
	AND	0FH		; AND IGNORE ALL OTHERS.
	ADD	A,B		; ADD THIS IN TO 'R1' BYTE.
	LD	B,A
	LD	HL,14		; GET THE 'S2' BYTE (EXTRA EXTENT).
	ADD	HL,DE
	LD	A,(HL)
	ADD	A,A		; AND SHIFT IT LEFT 4 BITS (BITS 4-7).
	ADD	A,A
	ADD	A,A
	ADD	A,A
	PUSH	AF		; SAVE CARRY FLAG (BIT 0 OF FLAG BYTE).
	ADD	A,B		; NOW ADD EXTRA EXTENT INTO 'R1'.
	LD	B,A
	PUSH	AF		; AND SAVE CARRY (OVERFLOW BYTE 'R2').
	POP	HL		; BIT 0 OF (L) IS THE OVERFLOW INDICATOR.
	LD	A,L
	POP	HL		; AND SAME FOR FIRST CARRY FLAG.
	OR	L		; EITHER ONE OF THESE SET?
	AND	01H		; ONLY CHECK THE CARRY FLAGS.
	RET
;
;   ROUTINE TO SETUP THE FCB (BYTES 'R0', 'R1', 'R2') TO
; REFLECT THE LAST RECORD USED FOR A RANDOM (OR OTHER) FILE.
; THIS READS THE DIRECTORY AND LOOKS AT ALL EXTENTS COMPUTING
; THE LARGERST RECORD NUMBER FOR EACH AND KEEPING THE MAXIMUM
; VALUE ONLY. THEN 'R0', 'R1', AND 'R2' WILL REFLECT THIS
; MAXIMUM RECORD NUMBER. THIS IS USED TO COMPUTE THE SPACE USED
; BY A RANDOM FILE.
;
RANSIZE:LD	C,12		; LOOK THRU DIRECTORY FOR FIRST ENTRY WITH
	CALL	FINDFST		; THIS NAME.
	LD	HL,(PARAMS)	; ZERO OUT THE 'R0, R1, R2' BYTES.
	LD	DE,33
	ADD	HL,DE
	PUSH	HL
	LD	(HL),D		; NOTE THAT (D)=0.
	INC	HL
	LD	(HL),D
	INC	HL
	LD	(HL),D
RANSIZ1:CALL	CKFILPOS	; IS THERE AN EXTENT TO PROCESS?
	JP	Z,RANSIZ3	; NO, WE ARE DONE.
	CALL	FCB2HL		; SET (HL) POINTING TO PROPER FCB IN DIR.
	LD	DE,15		; POINT TO LAST RECORD IN EXTENT.
	CALL	COMPRAND	; AND COMPUTE RANDOM PARAMETERS.
	POP	HL
	PUSH	HL		; NOW CHECK THESE VALUES AGAINST THOSE
	LD	E,A		; ALREADY IN FCB.
	LD	A,C		; THE CARRY FLAG WILL BE SET IF THOSE
	SUB	(HL)		; IN THE FCB REPRESENT A LARGER SIZE THAN
	INC	HL		; THIS EXTENT DOES.
	LD	A,B
	SBC	A,(HL)
	INC	HL
	LD	A,E
	SBC	A,(HL)
	JP	C,RANSIZ2
	LD	(HL),E		; WE FOUND A LARGER (IN SIZE) EXTENT.
	DEC	HL		; STUFF THESE VALUES INTO FCB.
	LD	(HL),B
	DEC	HL
	LD	(HL),C
RANSIZ2:CALL	FINDNXT		; NOW GET THE NEXT EXTENT.
	JP	RANSIZ1		; CONTINUE TIL ALL DONE.
RANSIZ3:POP	HL		; WE ARE DONE, RESTORE THE STACK AND
	RET			; RETURN.
;
;   FUNCTION TO RETURN THE RANDOM RECORD POSITION OF A GIVEN
; FILE WHICH HAS BEEN READ IN S.EQUENTIAL MODE UP TO NOW.
;
SETRAN:	LD	HL,(PARAMS)	; POINT TO FCB.
	LD	DE,32		; AND TO LAST USED RECORD.
	CALL	COMPRAND	; COMPUTE RANDOM POSITION.
	LD	HL,33		; NOW STUFF THESE VALUES INTO FCB.
	ADD	HL,DE
	LD	(HL),C		; MOVE 'R0'.
	INC	HL
	LD	(HL),B		; AND 'R1'.
	INC	HL
	LD	(HL),A		; AND LASTLY 'R2'.
	RET
;
;   THIS ROUTINE SELECT THE DRIVE SPECIFIED IN (ACTIVE) AND
; UPDATE THE LOGIN VECTOR AND BITMAP TABLE IF THIS DRIVE WAS
; NOT ALREADY ACTIVE.
;
LOGINDRV:
	LD	HL,(LOGIN)	; GET THE LOGIN VECTOR.
	LD	A,(ACTIVE)	; GET THE DEFAULT DRIVE.
	LD	C,A
	CALL	SHIFTR		; POSITION ACTIVE BIT FOR THIS DRIVE
	PUSH	HL		; INTO BIT 0.
	EX	DE,HL
	CALL	SELECT		; SELECT THIS DRIVE.
	POP	HL
	CALL	Z,SLCTERR	; VALID DRIVE?
	LD	A,L		; IS THIS A NEWLY ACTIVATED DRIVE?
	RRA
	RET	C
	LD	HL,(LOGIN)	; YES, UPDATE THE LOGIN VECTOR.
	LD	C,L
	LD	B,H
	CALL	SETBIT
	LD	(LOGIN),HL	; AND SAVE.
	JP	BITMAP		; NOW UPDATE THE BITMAP.
;
;   FUNCTION TO SET THE ACTIVE DISK NUMBER.
;
SETDSK:	LD	A,(EPARAM)	; GET PARAMETER PASSED AND SEE IF THIS
	LD	HL,ACTIVE	; REPRESENTS A CHANGE IN DRIVES.
	CP	(HL)
	RET	Z
	LD	(HL),A		; YES IT DOES, LOG IT IN.
	JP	LOGINDRV
;
;   THIS IS THE 'AUTO DISK SELECT' ROUTINE. THE FIRSST BYTE
; OF THE FCB IS EXAMINED FOR A DRIVE SPECIFICATION. IF NON
; ZERO THEN THE DRIVE WILL BE SELECTED AND LOGED IN.
;
AUTOSEL:LD	A,0FFH		; SAY 'AUTO-SELECT ACTIVATED'.
	LD	(AUTO),A
	LD	HL,(PARAMS)	; GET DRIVE SPECIFIED.
	LD	A,(HL)
	AND	1FH		; LOOK AT LOWER 5 BITS.
	DEC	A		; ADJUST FOR (1=A, 2=B) ETC.
	LD	(EPARAM),A	; AND SAVE FOR THE SELECT ROUTINE.
	CP	1EH		; CHECK FOR 'NO CHANGE' CONDITION.
	JP	NC,AUTOSL1	; YES, DON'T CHANGE.
	LD	A,(ACTIVE)	; WE MUST CHANGE, SAVE CURRENTLY ACTIVE
	LD	(OLDDRV),A	; DRIVE.
	LD	A,(HL)		; AND SAVE FIRST BYTE OF FCB ALSO.
	LD	(AUTOFLAG),A	; THIS MUST BE NON-ZERO.
	AND	0E0H		; WHATS THIS FOR (BITS 6,7 ARE USED FOR
	LD	(HL),A		; SOMETHING)?
	CALL	SETDSK		; SELECT AND LOG IN THIS DRIVE.
AUTOSL1:LD	A,(USERNO)	; MOVE USER NUMBER INTO FCB.
	LD	HL,(PARAMS)	; (* UPPER HALF OF FIRST BYTE *)
	OR	(HL)
	LD	(HL),A
	RET			; AND RETURN (ALL DONE).
;
;   FUNCTION TO RETURN THE CURRENT CP/M VERSION NUMBER.
;
GETVER:	LD	A,022H		; VERSION 2.2
	JP	SETSTAT
;
;   FUNCTION TO RESET THE DISK SYSTEM.
;
RSTDSK:	LD	HL,0		; CLEAR WRITE PROTECT STATUS AND LOG
	LD	(WRTPRT),HL	; IN VECTOR.
	LD	(LOGIN),HL
	XOR	A		; SELECT DRIVE 'A'.
	LD	(ACTIVE),A
	LD	HL,TBUFF	; SETUP DEFAULT DMA ADDRESS.
	LD	(USERDMA),HL
	CALL	DEFDMA
	JP	LOGINDRV	; NOW LOG IN DRIVE 'A'.
;
;   FUNCTION TO OPEN A SPECIFIED FILE.
;
OPENFIL:CALL	CLEARS2		; CLEAR 'S2' BYTE.
	CALL	AUTOSEL		; SELECT PROPER DISK.
	JP	OPENIT		; AND OPEN THE FILE.
;
;   FUNCTION TO CLOSE A SPECIFIED FILE.
;
CLOSEFIL:
	CALL	AUTOSEL		; SELECT PROPER DISK.
	JP	CLOSEIT		; AND CLOSE THE FILE.
;
;   FUNCTION TO RETURN THE FIRST OCCURENCE OF A SPECIFIED FILE
; NAME. IF THE FIRST BYTE OF THE FCB IS '?' THEN THE NAME WILL
; NOT BE CHECKED (GET THE FIRST ENTRY NO MATTER WHAT).
;
GETFST:	LD	C,0		; PREPARE FOR SPECIAL SEARCH.
	EX	DE,HL
	LD	A,(HL)		; IS FIRST BYTE A '?'?
	CP	'?'
	JP	Z,GETFST1	; YES, JUST GET VERY FIRST ENTRY (ZERO LENGTH MATCH).
	CALL	SETEXT		; GET THE EXTENSION BYTE FROM FCB.
	LD	A,(HL)		; IS IT '?'? IF YES, THEN WE WANT
	CP	'?'		; AN ENTRY WITH A SPECIFIC 'S2' BYTE.
	CALL	NZ,CLEARS2	; OTHERWISE, LOOK FOR A ZERO 'S2' BYTE.
	CALL	AUTOSEL		; SELECT PROPER DRIVE.
	LD	C,15		; COMPARE BYTES 0-14 IN FCB (12&13 EXCLUDED).
GETFST1:CALL	FINDFST		; FIND AN ENTRY AND THEN MOVE IT INTO
	JP	MOVEDIR		; THE USERS DMA SPACE.
;
;   FUNCTION TO RETURN THE NEXT OCCURENCE OF A FILE NAME.
;
GETNXT:	LD	HL,(SAVEFCB)	; RESTORE POINTERS. NOTE THAT NO
	LD	(PARAMS),HL	; OTHER DBOS CALLS ARE ALLOWED.
	CALL	AUTOSEL		; NO ERROR WILL BE RETURNED, BUT THE
	CALL	FINDNXT		; RESULTS WILL BE WRONG.
	JP	MOVEDIR
;
;   FUNCTION TO DELETE A FILE BY NAME.
;
DELFILE:CALL	AUTOSEL		; SELECT PROPER DRIVE.
	CALL	ERAFILE		; ERASE THE FILE.
	JP	STSTATUS	; SET STATUS AND RETURN.
;
;   FUNCTION TO EXECUTE A S.EQUENTIAL READ OF THE SPECIFIED
; RECORD NUMBER.
;
READSEQ:CALL	AUTOSEL		; SELECT PROPER DRIVE THEN READ.
	JP	RDSEQ
;
;   FUNCTION TO WRITE THE NET S.EQUENTIAL RECORD.
;
WRTSEQ:	CALL	AUTOSEL		; SELECT PROPER DRIVE THEN WRITE.
	JP	WTSEQ
;
;   CREATE A FILE FUNCTION.
;
FCREATE:CALL	CLEARS2		; CLEAR THE 'S2' BYTE ON ALL CREATES.
	CALL	AUTOSEL		; SELECT PROPER DRIVE AND GET THE NEXT
	JP	GETEMPTY	; EMPTY DIRECTORY SPACE.
;
;   FUNCTION TO RENAME A FILE.
;
RENFILE:CALL	AUTOSEL		; SELECT PROPER DRIVE AND THEN SWITCH
	CALL	CHGNAMES	; FILE NAMES.
	JP	STSTATUS
;
;   FUNCTION TO RETURN THE LOGIN VECTOR.
;
GETLOG:	LD	HL,(LOGIN)
	JP	GETPRM1
;
;   FUNCTION TO RETURN THE CURRENT DISK ASSIGNMENT.
;
GETCRNT:LD	A,(ACTIVE)
	JP	SETSTAT
;
;   FUNCTION TO SET THE DMA ADDRESS.
;
PUTDMA:	EX	DE,HL
	LD	(USERDMA),HL	; SAVE IN OUR SPACE AND THEN GET TO
	JP	DEFDMA		; THE BIOS WITH THIS ALSO.
;
;   FUNCTION TO RETURN THE ALLOCATION VECTOR.
;
GETALOC:LD	HL,(ALOCVECT)
	JP	GETPRM1
;
;   FUNCTION TO RETURN THE READ-ONLY STATUS VECTOR.
;
GETROV:	LD	HL,(WRTPRT)
	JP	GETPRM1
;
;   FUNCTION TO SET THE FILE ATTRIBUTES (READ-ONLY, SYSTEM).
;
SETATTR:CALL	AUTOSEL		; SELECT PROPER DRIVE THEN SAVE ATTRIBUTES.
	CALL	SAVEATTR
	JP	STSTATUS
;
;   FUNCTION TO RETURN THE ADDRESS OF THE DISK PARAMETER BLOCK
; FOR THE CURRENT DRIVE.
;
GETPARM:LD	HL,(DISKPB)
GETPRM1:LD	(STATUS),HL
	RET
;
;   FUNCTION TO GET OR SET THE USER NUMBER. IF (E) WAS (FF)
; THEN THIS IS A REQUEST TO RETURN THE CURRENT USER NUMBER.
; ELSE SET THE USER NUMBER FROM (E).
;
GETUSER:LD	A,(EPARAM)	; GET PARAMETER.
	CP	0FFH		; GET USER NUMBER?
	JP	NZ,SETUSER
	LD	A,(USERNO)	; YES, JUST DO IT.
	JP	SETSTAT
SETUSER:AND	1FH		; NO, WE SHOULD SET IT INSTEAD. KEEP LOW
	LD	(USERNO),A	; BITS (0-4) ONLY.
	RET
;
;   FUNCTION TO READ A RANDOM RECORD FROM A FILE.
;
RDRANDOM:
	CALL	AUTOSEL		; SELECT PROPER DRIVE AND READ.
	JP	READRAN
;
;   FUNCTION TO COMPUTE THE FILE SIZE FOR RANDOM FILES.
;
WTRANDOM:
	CALL	AUTOSEL		; SELECT PROPER DRIVE AND WRITE.
	JP	WRITERAN
;
;   FUNCTION TO COMPUTE THE SIZE OF A RANDOM FILE.
;
FILESIZE:
	CALL	AUTOSEL		; SELECT PROPER DRIVE AND CHECK FILE LENGTH
	JP	RANSIZE
;
;   FUNCTION #37. THIS ALLOWS A PROGRAM TO LOG OFF ANY DRIVES.
; ON ENTRY, SET (DE) TO CONTAIN A WORD WITH BITS SET FOR THOSE
; DRIVES THAT ARE TO BE LOGGED OFF. THE LOG-IN VECTOR AND THE
; WRITE PROTECT VECTOR WILL BE UPDATED. THIS MUST BE A M/PM
; SPECIAL FUNCTION.
;
LOGOFF:	LD	HL,(PARAMS)	; GET DRIVES TO LOG OFF.
	LD	A,L		; FOR EACH BIT THAT IS SET, WE WANT
	CPL			; TO CLEAR THAT BIT IN (LOGIN)
	LD	E,A		; AND (WRTPRT).
	LD	A,H
	CPL
	LD	HL,(LOGIN)	; RESET THE LOGIN VECTOR.
	AND	H
	LD	D,A
	LD	A,L
	AND	E
	LD	E,A
	LD	HL,(WRTPRT)
	EX	DE,HL
	LD	(LOGIN),HL	; AND SAVE.
	LD	A,L		; NOW DO THE WRITE PROTECT VECTOR.
	AND	E
	LD	L,A
	LD	A,H
	AND	D
	LD	H,A
	LD	(WRTPRT),HL	; AND SAVE. ALL DONE.
	RET
;
;   GET HERE TO RETURN TO THE USER.
;
GOBACK:	LD	A,(AUTO)	; WAS AUTO SELECT ACTIVATED?
	OR	A
	JP	Z,GOBACK1
	LD	HL,(PARAMS)	; YES, BUT WAS A CHANGE MADE?
	LD	(HL),0		; (* RESET FIRST BYTE OF FCB *)
	LD	A,(AUTOFLAG)
	OR	A
	JP	Z,GOBACK1
	LD	(HL),A		; YES, RESET FIRST BYTE PROPERLY.
	LD	A,(OLDDRV)	; AND GET THE OLD DRIVE AND SELECT IT.
	LD	(EPARAM),A
	CALL	SETDSK
GOBACK1:LD	HL,(USRSTACK)	; RESET THE USERS STACK POINTER.
	LD	SP,HL
	LD	HL,(STATUS)	; GET RETURN STATUS.
	LD	A,L		; FORCE VERSION 1.4 COMPATABILITY.
	LD	B,H
	RET			; AND GO BACK TO USER.
;
;   FUNCTION #40. THIS IS A SPECIAL ENTRY TO DO RANDOM I/O.
; FOR THE CASE WHERE WE ARE WRITING TO UNUSED DISK SPACE, THIS
; SPACE WILL BE ZEROED OUT FIRST. THIS MUST BE A M/PM SPECIAL
; PURPOSE FUNCTION, BECAUSE WHY WOULD ANY NORMAL PROGRAM EVEN
; CARE ABOUT THE PREVIOUS CONTENTS OF A SECTOR ABOUT TO BE
; WRITTEN OVER.
;
WTSPECL:CALL	AUTOSEL		; SELECT PROPER DRIVE.
	LD	A,2		; USE SPECIAL WRITE MODE.
	LD	(MODE),A
	LD	C,0		; SET WRITE INDICATOR.
	CALL	POSITN1		; POSITION THE FILE.
	CALL	Z,WTSEQ1	; AND WRITE (IF NO ERRORS).
	RET
;
;**************************************************************
;*
;*     BDOS DATA STORAGE POOL.
;*
;**************************************************************
;
EMPTYFCB:
	.DB 0E5H		; EMPTY DIRECTORY SEGMENT INDICATOR.
WRTPRT:	.DW 0			; WRITE PROTECT STATUS FOR ALL 16 DRIVES.
LOGIN:	.DW 0			; DRIVE ACTIVE WORD (1 BIT PER DRIVE).
USERDMA:.DW 080H		; USER'S DMA ADDRESS (DEFAULTS TO 80H).
;
;   SCRATCH AREAS FROM PARAMETER BLOCK.
;
SCRATCH1:
	.DW 0			; RELATIVE POSITION WITHIN DIR SEGMENT FOR FILE (0-3).
SCRATCH2:
	.DW 0			; LAST SELECTED TRACK NUMBER.
SCRATCH3:
	.DW 0			; LAST SELECTED SECTOR NUMBER.
;
;   DISK STORAGE AREAS FROM PARAMETER BLOCK.
;
DIRBUF:	.DW 0			; ADDRESS OF DIRECTORY BUFFER TO USE.
DISKPB:	.DW 0			; CONTAINS ADDRESS OF DISK PARAMETER BLOCK.
CHKVECT:.DW 0			; ADDRESS OF CHECK VECTOR.
ALOCVECT:
	.DW 0			; ADDRESS OF ALLOCATION VECTOR (BIT MAP).
;
;   PARAMETER BLOCK RETURNED FROM THE BIOS.
;
SECTORS:.DW 0			; SECTORS PER TRACK FROM BIOS.
BLKSHFT:.DB 0			; BLOCK SHIFT.
BLKMASK:.DB 0			; BLOCK MASK.
EXTMASK:.DB 0			; EXTENT MASK.
DSKSIZE:.DW 0			; DISK SIZE FROM BIOS (NUMBER OF BLOCKS-1).
DIRSIZE:.DW 0			; DIRECTORY SIZE.
ALLOC0:	.DW 0			; STORAGE FOR FIRST BYTES OF BIT MAP (DIR SPACE USED).
ALLOC1:	.DW 0
OFFSET:	.DW 0			; FIRST USABLE TRACK NUMBER.
XLATE:	.DW 0			; SECTOR TRANSLATION TABLE ADDRESS.
CLOSEFLG:
	.DB 0			; CLOSE FLAG (=0FFH IS EXTENT WRITTEN OK).
RDWRTFLG:
	.DB 0			; READ/WRITE FLAG (0FFH=READ, 0=WRITE).
FNDSTAT:.DB 0			; FILENAME FOUND STATUS (0=FOUND FIRST ENTRY).
MODE:	.DB 0			; I/O MODE SELECT (0=RANDOM, 1=S.EQUENTIAL, 2=SPECIAL RANDOM).
EPARAM:	.DB 0			; STORAGE FOR REGISTER (E) ON ENTRY TO BDOS.
RELBLOCK:
	.DB 0			; RELATIVE POSITION WITHIN FCB OF BLOCK NUMBER WRITTEN.
COUNTER:.DB 0			; BYTE COUNTER FOR DIRECTORY NAME SEARCHES.
SAVEFCB:.DW 0,0			; SAVE SPACE FOR ADDRESS OF FCB (FOR DIRECTORY SEARCHES).
BIGDISK:.DB 0			; IF =0 THEN DISK IS > 256 BLOCKS LONG.
AUTO:	.DB 0			; IF NON-ZERO, THEN AUTO SELECT ACTIVATED.
OLDDRV:	.DB 0			; ON AUTO SELECT, STORAGE FOR PREVIOUS DRIVE.
AUTOFLAG:
	.DB 0			; IF NON-ZERO, THEN AUTO SELECT CHANGED DRIVES.
SAVNXT:	.DB 0			; STORAGE FOR NEXT RECORD NUMBER TO ACCESS.
SAVEXT:	.DB 0			; STORAGE FOR EXTENT NUMBER OF FILE.
SAVNREC:.DW 0			; STORAGE FOR NUMBER OF RECORDS IN FILE.
BLKNMBR:.DW 0			; BLOCK NUMBER (PHYSICAL SECTOR) USED WITHIN A FILE OR LOGICAL SEC
LOGSECT:.DW 0			; STARTING LOGICAL (128 BYTE) SECTOR OF BLOCK (PHYSICAL SECTOR).
FCBPOS:	.DB 0			; RELATIVE POSITION WITHIN BUFFER FOR FCB OF FILE OF INTEREST.
FILEPOS:.DW 0			; FILES POSITION WITHIN DIRECTORY (0 TO MAX ENTRIES -1).
;
;   DISK DIRECTORY BUFFER CHECKSUM BYTES. ONE FOR EACH OF THE
; 16 POSSIBLE DRIVES.
;
CKSUMTBL:
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;
;   EXTRA SPACE ?
;
	.DB 0,0,0,0
;
;**************************************************************
;*
;*        B I O S   J U M P   T A B L E
;*
;**************************************************************
;
;BOOT	JMP	0	;NOTE WE USE FAKE DESTINATIONS
;WBOOT	JMP	0
;CONST	JMP	0
;CONIN	JMP	0
;CONOUT	JMP	0
;LIST	JMP	0
;PUNCH	JMP	0
;READER	JMP	0
;HOME	JMP	0
;SELDSK	JMP	0
;SETTRK	JMP	0
;SETSEC	JMP	0
;SETDMA	JMP	0
;READ	JMP	0
;WRITE	JMP	0
;PRSTAT	JMP	0
;SECTRN	JMP	0
;
;*
;******************   E N D   O F   C P / M   *****************
;*

;	.END


;
;**************************************************************
;*
;*        C B I O S  f o r
;*
;*  T e s t   P r o t o t y p e
;*
;*  by Andrew Lynch, with input from many sources
;*
;**************************************************************
;
;	SKELETAL CBIOS FOR FIRST LEVEL OF CP/M 2.0 ALTERATION
;             WITH MODS FOR CP/M  ROMDISK AND RAMDISK.
;
;             ENTIRELY IN 8080 MNEUMONICS (SO ASM CAN BE USED)
;             BUT ASSUMES A Z80! (remove)
;
;MSIZE	.EQU	20			;CP/M VERSION MEMORY SIZE IN KILOBYTES
;MSIZE	.EQU	62			;CP/M VERSION MEMORY SIZE IN KILOBYTES
; MEM defined in CPM22 above, line 0015
MSIZE:	.EQU MEM		; CP/M VERSION MEMORY SIZE IN KILOBYTES

;
;	"BIAS" IS ADDRESS OFFSET FROM 3400H FOR MEMORY SYSTEMS
;	THAN 16K (REFERRED TO AS "B" THROUGHOUT THE TEXT).
;
BIAS:	.EQU (MSIZE-20)*1024
CCP:	.EQU 3400H+BIAS		; BASE OF CCP
BDOS:	.EQU CCP+806H		; BASE OF BDOS
BIOS:	.EQU CCP+1600H		; BASE OF BIOS
CDISK:	.EQU 0004H		; CURRENT DISK NUMBER 0=A,...,15=P

     		.ORG   BIOS

; IOBYTE already defined in CPM22 above, line 0017
;IOBYTE	.EQU	0003H			;INTEL I/O BYTE
;
;	CONSTANTS

END:		.EQU 0FFH
;CR		.EQU	0DH
;LF		.EQU	0AH

; TEST PROTOTYPE SPECIFIC HARDWARE IO PORT ADDRESSES AND MEMORY LOCATIONS

UART:		.EQU 68H		; BASE IO ADDRESS OF UART
MPCL_RAM:	.EQU 78H		; BASE IO ADDRESS OF RAM MEMORY PAGER CONFIGURATION LATCH
MPCL_ROM:	.EQU 7CH		; BASE IO ADDRESS OF ROM MEMORY PAGER CONFIGURATION LATCH

ROMSTART_CPM:	.EQU 1200H		; WHERE THE CCP+BDOS+BIOS IS STORED IN ROM
RAMTARG_CPM:	.EQU 0D400H		; WHERE THE CCP+BDOS+BIOS STARTS IN RAM (ENTRY POINT)
MOVSIZ_CPM:	.EQU 2BFFH		; CCP, BDOS, + BIOS IS 6-7KB IN LENGTH
CCPSIZ_CPM:	.EQU 0800H		; CCP $800 BYTES IN LENGTH

; IDE REGISTER		IO PORT		; FUNCTION
IDELO:		.EQU 20H		; DATA PORT (LOW BYTE)
IDEERR:		.EQU 21H		; READ: ERROR REGISTER; WRITE: PRECOMP
IDESECTC:	.EQU 22H		; SECTOR COUNT
IDESECTN:	.EQU 23H		; SECTOR NUMBER
IDECYLLO:	.EQU 24H		; CYLINDER LOW
IDECYLHI:	.EQU 25H		; CYLINDER HIGH
IDEHEAD:	.EQU 26H		; DRIVE/HEAD
IDESTTS:	.EQU 27H		; READ: STATUS; WRITE: COMMAND
IDEHI:		.EQU 28H		; DATA PORT (HIGH BYTE)
IDECTRL:	.EQU 2EH		; READ: ALTERNATIVE STATUS; WRITE; DEVICE CONTROL
IDEADDR:	.EQU 2FH		; DRIVE ADDRESS (READ ONLY)
FMSR:		.EQU	036H		; ADDRESS OF MAIN STATUS REGISTER
FDATA:		.EQU	037H		; FLOPPY DATA REGISTER
FLATCH:		.EQU	03AH		; FLOPPY CONFIGURATION LATCH
FDMA:		.EQU	03CH		; PSEUDO DMA ADDRESS
;
; FDC CONFIGURATION LATCH OUTPUT BIT PATTERNS
MOTOR:		.EQU	%00000000	; BIT PATTERN IN LATCH FOR MOTOR CONTROL (ON)
TERMCN:		.EQU	%00000001	; BIT PATTERN IN LATCH TO WRITE A TC STROBE
RESETL:		.EQU	%00000010	; BIT PATTERN IN LATCH TO RESET ALL BITS
MINI:		.EQU	%00000100	; BIT PATTERN IN LATCH TO SET MINI MODE FDC9229 LOW DENS=1, HIGH DENS=0
PRECOMP:	.EQU	%00100000	; BIT PATTERN IN LATCH TO SET WRITE PRECOMP 125 NS:
FDDENSITY:	.EQU	%01000000	; BIT PATTERN IN LATCH TO FLOPPY LOW DENSITY (HIGH IS 0)
FDREADY:	.EQU	%10000000	; BIT PATTERN IN LATCH TO FLOPPY READY (P-34):



;	.ORG	BIOS			;ORIGIN OF THIS PROGRAM
NSECTS:	.EQU ($-CCP)/128	; WARM START SECTOR COUNT

;
;	JUMP VECTOR FOR INDIVIDUAL SUBROUTINES
	JP	BOOT		; COLD START
WBOOTE:	JP	WBOOT		; WARM START
	JP	CONST		; CONSOLE STATUS
	JP	CONIN		; CONSOLE CHARACTER IN
	JP	CONOUT		; CONSOLE CHARACTER OUT
	JP	LIST		; LIST CHARACTER OUT (NULL ROUTINE)
	JP	PUNCH		; PUNCH CHARACTER OUT (NULL ROUTINE)
	JP	READER		; READER CHARACTER OUT (NULL ROUTINE)
	JP	HOME		; MOVE HEAD TO HOME POSITION
	JP	SELDSK		; SELECT DISK
	JP	SETTRK		; SET TRACK NUMBER
	JP	SETSEC		; SET SECTOR NUMBER
	JP	SETDMA		; SET DMA ADDRESS
	JP	READ		; READ DISK
	JP	WRITE		; WRITE DISK
	JP	LISTST		; RETURN LIST STATUS (NULL ROUTINE)
	JP	SECTRN		; SECTOR TRANSLATE
;
;   FIXED DATA TABLES FOR ALL DRIVES
;   0= FLOPPY DISK, 1=RAMDISK, 2=IDE, 3=ATAPI, AND 4=HDPART4
;   5= 1MBDISK

	.IF CONDSWAPAB

		;   DISK PARAMETER HEADER FOR DISK 00
DPBASE:		.DW 0000,0000
		.DW 0000,0000
		.DW DIRBF,DPBLK0
		.DW CHK00,ALL00
		;   DISK PARAMETER HEADER FOR DISK 01
		.DW 0000,0000
		.DW 0000,0000
		.DW DIRBF,DPBLK1
		.DW CHK01,ALL01

	.ELSE

		;   DISK PARAMETER HEADER FOR DISK 01
DPBASE:		.DW 0000,0000
		.DW 0000,0000
		.DW DIRBF,DPBLK1
		.DW CHK01,ALL01

		;   DISK PARAMETER HEADER FOR DISK 00
		.DW 0000,0000
		.DW 0000,0000
		.DW DIRBF,DPBLK0
		.DW CHK00,ALL00

	.ENDIF

;   DISK PARAMETER HEADER FOR DISK 02
	.DW 0000,0000
	.DW 0000,0000
	.DW DIRBF,DPBLK2
	.DW CHK02,ALL02
;   DISK PARAMETER HEADER FOR DISK 03
	.DW 0000,0000
	.DW 0000,0000
	.DW DIRBF,DPBLK3
	.DW CHK03,ALL03
;   DISK PARAMETER HEADER FOR DISK 04
	.DW 0000,0000
	.DW 0000,0000
	.DW DIRBF,DPBLK4
	.DW CHK04,ALL04
;   DISK PARAMETER HEADER FOR DISK 05
	.DW 0000,0000
	.DW 0000,0000
	.DW DIRBF,DPBLK5
	.DW CHK05,ALL05
;   DISK PARAMETER HEADER FOR DISK 06
	.DW 0000,0000
	.DW 0000,0000
	.DW DIRBF,DPBLK6
	.DW CHK06,ALL06

;

DPBLK0:				; DISK PARAMETER BLOCK (FLOPPY DISK 720KB)
SPT_0:	.DW  36			; 36 SECTORS OF 128 BYTES PER 4.5K TRACK
BSH_0:	.DB  4			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_0:	.DB  15			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_0:	.DB  0			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_0:	.DW  350		; BLOCKSIZE [2048] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
DRM_0:	.DW  127		; NUMBER OF DIRECTORY ENTRIES
AL0_0:	.DB  11000000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_0:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_0:	.DW  32			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_0:	.DW  4			; FIRST 4 TRACKS TRACKS RESERVED (18K FOR SYSTEM)
				; 
DPBLK1:				; DISK PARAMETER BLOCK (RAMDISK 512K, 448K usable)
SPT_1:	.DW  256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
BSH_1:	.DB  4			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_1:	.DB  15			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_1:	.DB  1			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_1:	.DW  225		; BLOCKSIZE [2048] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
DRM_1:	.DW  255		; NUMBER OF DIRECTORY ENTRIES
AL0_1:	.DB  11110000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_1:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_1:	.DW  0			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_1:	.DW  1			; 1 TRACK RESERVED [FIRST 32K OF RAM]
				;
DPBLK2:				; DISK PARAMETER BLOCK (IDE HARD DISK 8MB)
SPT_2:	.DW  256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
BSH_2:	.DB  5			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_2:	.DB  31			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_2:	.DB  1			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_2:	.DW  2017		; BLOCKSIZE [4096] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
DRM_2:	.DW  511		; NUMBER OF DIRECTORY ENTRIES
AL0_2:	.DB  11110000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_2:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_2:	.DW  0			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_2:	.DW  1			; 1 TRACK (32K) RESERVED FOR SYSTEM
				;
DPBLK3:				; DISK PARAMETER BLOCK (ATAPI DRIVE 8MB)
SPT_3:	.DW  256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
BSH_3:	.DB  5			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_3:	.DB  31			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_3:	.DB  1			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_3:	.DW  2017		; BLOCKSIZE [4096] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
DRM_3:	.DW  511		; NUMBER OF DIRECTORY ENTRIES
AL0_3:	.DB  11110000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_3:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_3:	.DW  0			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_3:	.DW  1			; 1 TRACK (32K) RESERVED FOR SYSTEM
				;
DPBLK4:				; DISK PARAMETER BLOCK (IDE HARD DISK 1024K)
SPT_4:	.DW  256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
BSH_4:	.DB  4			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_4:	.DB  15			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_4:	.DB  1			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_4:	.DW  497		; BLOCKSIZE [2048] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
DRM_4:	.DW  255		; NUMBER OF DIRECTORY ENTRIES
AL0_4:	.DB  11110000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_4:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_4:	.DW  0			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_4:	.DW  1			; 1 TRACK RESERVED [FIRST 32K OF PARTITION]
				;
DPBLK5:				; DISK PARAMETER BLOCK (ROMDISK 1MB) ** RESERVED FOR FUTURE EXPANSION 
SPT_5:	.DW  256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
BSH_5:	.DB  4			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_5:	.DB  15			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_5:	.DB  1			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_5:	.DW  511		; BLOCKSIZE [2048] * NUMBER OF BLOCKS +1 =DRIVE SIZE
DRM_5:	.DW  255		; NUMBER OF DIRECTORY ENTRIES
AL0_5:	.DB  11110000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_5:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_5:	.DW  0			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_5:	.DW  1			; 1 TRACK RESERVED [FIRST 32K OF ROM]
				;
DPBLK6:				;DISK PARAMETER BLOCK (ROMDISK 1MB) ** RESERVED FOR FUTURE EXPANSION *
SPT_6:	.DW 	16	 	; 16 SECTORS OF 128 BYTES PER 2K TRACK
BSH_6:	.DB 	3 		; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_6:	.DB 	7 		; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_6:	.DB 	1 		; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_6:	.DW 	31 		; BLOCKSIZE [1024] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
DRM_6:	.DW 	31 		; NUMBER OF DIRECTORY ENTRIES
AL0_6:	.DB 	10000000  	; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_6:	.DB 	00000000  	; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_6:	.DW 	0 	  	; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_6:	.DW 	8 	  	; FIRST 5 TRACKS TRACKS RESERVED (16K FOR SYSTEM)
				; SYSTEM IS ROM LOADER, CCP, BDOS, CBIOS, AND MONITOR
				;
				; IMPORTANT NOTE: TRACKS $00 - $04 OF 2K BYTES
				; EACH ARE MARKED WITH THE OFF_0 SET TO 5 AS 
				; SYSTEM TRACKS. USABLE ROM DRIVE SPACE
				; STARTING AFTER THE FIFTH TRACK (IE, TRACK $05)
				; MOST LIKELY FIX TO THIS IS PLACING A DUMMY
				; FIRST 10K ROM CONTAINS THE ROM LOADER, MONITOR,
 				; CCP, BDOS, BIOS, ETC (5 TRACKS * 2K EACH)



;
;	END OF FIXED TABLES
;
;	INDIVIDUAL SUBROUTINES TO PERFORM EACH FUNCTION

BOOT:				; SIMPLEST CASE IS TO JUST PERFORM PARAMETER INITIALIZATION
				;
	LD	A,80H		; LOAD VALUE TO SWITCH OUT ROM
	OUT	(MPCL_ROM),A	; SWITCH OUT ROM, BRING IN LOWER 32K RAM PAGE
				;
				;
	LD	A,%10000001	; switch in first 32K lower page (first track)
	OUT	(MPCL_RAM),A	;
				; Formatting the ram is simple as clearing the directory area 
				; to a value of E5h (the first 8k of track 1 or the ramdisk).
	LD	HL,0000H	; starting memory address of track 1, sector 0 in HL
	LD	BC,1FFFH	; 8K of directory sectors reserved (length in BC)
	LD	A,0E5H		; initializing value in A 
	LD	E,L		;
	LD	D,H		;
	INC	DE		;
	LD	(HL),A		;
	LDIR			;
				;
	LD	A,80H		; LOAD VALUE TO SWITCH OUT ROM
	OUT	(MPCL_ROM),A	; SWITCH OUT ROM, BRING IN LOWER 32K RAM PAGE

	LD	A,00H		; ENSURE LOWEST RAM PAGE SELECTED
	OUT	(MPCL_RAM),A	; BRING IN LOWEST 32K RAM PAGE

	XOR	A		; ZERO IN THE ACCUM
	LD	(IOBYTE),A	; CLEAR THE IOBYTE
	LD	(CDISK),A	; SELECT DISK ZERO
	
	CALL	IDE_SOFT_RESET	; RESET THE IDE HARD DISK

	LD	HL,TXT_STARTUP_MSG ; PRINT STARTUP MESSAGE
	CALL	PRTMSG

	JP	GOCPM		; INITIALIZE AND GO TO CP/M

;
WBOOT:				; SIMPLEST CASE IS TO READ THE DISK UNTIL ALL SECTORS LOADED
				; WITH A ROMDISK WE SELECT THE ROM AND THE CORRECT PAGE [0]
				; THEN COPY THE CP/M IMAGE (CCP, BDOS, BIOS, MONITOR) TO HIGH RAM
				; LOAD ADDRESS.
				; FOR Z80 IT LOOKS LIKE THIS... USING 8080 NEMONICS

	DI			; DISABLE INTERRUPT
	LD	SP,80H		; USE SPACE BELOW BUFFER FOR STACK
	IM	1		; SET INTERRUPT MODE 1

	XOR	A		; CHEAP ZERO IN ACC
	OUT	(MPCL_ROM),A	; SEND 0 TO ROM MAP PORT (SWITCH IN LOWER 32K ROM PAGE)

	XOR	A		; CHEAP ZERO IN ACC
	OUT	(MPCL_RAM),A	; SEND 0 TO RAM MAP PORT (SELECT LOWEST RAM PAGE)


; REMOVED FOR DISK BOOT
;
;	IF 	CONDWBOOT
;		LD	HL,ROMSTART_MON	; WHERE IN ROM MONITOR IS STORED (FIRST BYTE)
;		LD	DE,RAMTARG_MON	; WHERE IN RAM TO MOVE MONITOR TO (FIRST BYTE)
;		LD	BC,MOVSIZ_MON	; NUMBER OF BYTES TO MOVE FROM ROM TO RAM
;		LDIR
;
;		LD	HL,ROMSTART_CPM	; WHERE IN ROM CP/M IS STORED (FIRST BYTE)
;		LD	DE,RAMTARG_CPM	; WHERE IN RAM TO MOVE MONITOR TO (FIRST BYTE)
;		LD	BC,MOVSIZ_CPM	; NUMBER OF BYTES TO MOVE FROM ROM TO RAM
;		LDIR
;	ELSE
;		LD 	HL,0F2F0H	;move from F2F0 the 5 TSR jumps
;		LD 	DE,0FFF0H	;move to FFF0
;		LD	BC,16		; 16 BYTES
;		LDIR
;
;		LD	HL,ROMSTART_CPM	; WHERE IN ROM CP/M IS STORED (FIRST BYTE)
;		LD	DE,RAMTARG_CPM	; WHERE IN RAM TO MOVE MONITOR TO (FIRST BYTE)
;		LD	BC,MOVSIZ_CPM	; NUMBER OF BYTES TO MOVE FROM ROM TO RAM
;		LDIR
;
;		LD	HL,0FFF0H	; move from FFF0
;		LD	DE,0F2F0H	; move back to F2F0
;		LD 	BC,16		; 16 bytes
;		LDIR
;	ENDIF

		LD	HL,ROMSTART_CPM	; WHERE IN ROM CP/M IS STORED (FIRST BYTE)
		LD	DE,RAMTARG_CPM	; WHERE IN RAM TO MOVE MONITOR TO (FIRST BYTE)
		LD	BC,CCPSIZ_CPM	; NUMBER OF BYTES TO MOVE FROM ROM TO RAM
		LDIR


	EI			; ENABLE INTERRUPTS (ACCESS TO MONITOR WHILE
				; CP/M RUNNING)
	LD	A,80H		; LOAD VALUE TO SWITCH OUT ROM
	OUT	(MPCL_ROM),A	; SWITCH OUT ROM, BRING IN LOWER 32K RAM PAGE

	XOR	A		; CHEAP ZERO IN ACC
	OUT	(MPCL_RAM),A	; SEND 0 TO RAM MAP PORT (SELECT LOWEST RAM PAGE)

	CALL	IDE_SOFT_RESET	; RESET THE IDE HARD DISK

	.IF CONDSUPERSUB
		; do nothing for original code
	.ELSE
		; clear the autosub buffer, do on a warm boot
		XOR	A
		LD	(INBUFF+1),A	; SECOND BYTE IS ACTUAL LENGTH.

	.ENDIF

	LD	HL,TXT_STARTUP_MSG ; PRINT STARTUP MESSAGE
	CALL	PRTMSG
				; FALL THROUGH TO GOCPM ROUTINE

;	END OF LOAD OPERATION, SET PARAMETERS AND GO TO CP/M
GOCPM:
				; CPU RESET HANDLER
	LD	A,0C3H		; C3 IS A JMP INSTRUCTION
	LD	(0000H),A	; FOR JMP TO WBOOT
	LD	HL,WBOOTE	; WBOOT ENTRY POINT
	LD	(1),HL		; SET ADDRESS FIELD FOR JMP AT 0
;
				; CPU INTERRUPT HANDLER
	LD	A,0C3H		; C3 IS A JMP INSTRUCTION
	LD	(0038H),A	; FOR JMP TO WBOOT
	LD	HL,WBOOTE	; WBOOT ENTRY POINT
	LD	(1),HL		; SET ADDRESS FIELD FOR JMP AT 0
;
	LD	(5),A		; FOR JMP TO BDOS
	LD	HL,BDOS		; BDOS ENTRY POINT
	LD	(6),HL		; ADDRESS FIELD OF JUMP AT 5 TO BDOS
;
	LD	BC,80H		; DEFAULT DMA ADDRESS IS 80H
	CALL	SETDMA
;
	CALL	SETUPDRIVE	; SETUP FLOPPY PARAMETERS
;
	LD	A,(CDISK)	; GET CURRENT DISK NUMBER
	LD	C,A		; SEND TO THE CCP
	JP	CCP		; GO TO CP/M FOR FURTHER PROCESSING
;
;
;	SIMPLE I/O HANDLERS (MUST BE FILLED IN BY USER)
;	IN EACH CASE, THE ENTRY POINT IS PROVIDED, WITH SPACE RESERVED
;	TO INSERT YOUR OWN CODE
;

;	IF CONDKEYBOARD

CONST:					; CONSOLE STATUS, RETURN 0FFH IF CHARACTER READY, 00H IF NOT
		IN	A,(UART + 05H)	; READ LINE STATUS REGISTER (UART5 = $68 + $05)
		AND	01H		; TEST IF DATA IN RECEIVE BUFFER
					; IS THERE A CHAR READY? 0=NO, 1=YES
		JP	Z,NOT_READY
		LD	A,0FFH		; YES, PUT $FF IN A AND RETURN
NOT_READY:
					; NO, LEAVE $00 IN A AND RETURN
		RET
CONIN:					; CONSOLE CHARACTER INTO REGISTER A

		CALL	CONST		; IS A CHAR READY TO BE READ FROM UART?
		CP	00H		; 
		JP	Z,CONIN		; NO?  TRY AGAIN   
		IN	A,(UART)	; YES? READ THE CHAR FROM THE UART (UART0 = $68 + $00)
					; REGISTER AND PASS BACK TO USER
		.IF CONDMASKCONIN
					; no change if true leave original code
		.ELSE
		AND	127		; mask out ascii 128 to 255 = rubbish that might come from an xmodem fail
		.ENDIF
		RET
	
;	ELSE
;		; scan keyboard as well (need to run TSR keyboard)
;CONST:		CALL	$F2F3		; counter, used for a clock and for random number gen
;					; CONSOLE STATUS, RETURN 0FFH IF CHARACTER READY, 00H IF NOT
;		IN	A,(UART + $05)	; READ LINE STATUS REGISTER (UART5 = $68 + $05)
;		AND	$01		; TEST IF DATA IN RECEIVE BUFFER
;					; IS THERE A CHAR READY? 0=NO, 1=YES
;		CALL 	Z,$F2F6		; no character, so try PS/2 keyboard TSR
;		CP	$FF		; anything from the keyboard? FF=yes (need to call again with A=1 to get)
;		RET	Z		; return if yes
;		RET

;CONIN:		LD	A,0		; start with nothing
;		CALL	CONST		; returns A=0 or A=not 0
;		CP	0		; keep looping while it is a zero
;		JR	Z,CONIN		; if nothing, keep trying
;					; if something, retest because is it keyboard or uart?
;		IN	A,(UART + $05)	; READ LINE STATUS REGISTER (UART5 = $68 + $05)
;		AND	$01		; TEST IF DATA IN RECEIVE BUFFER
;		JR	NZ,CONUART	; if not a zero then get the data from the uart
;					; if got here it must be the keyboard 
;		LD	A,1		; tells the keyboard TSR to return the ascii character
;		CALL	$F2F6		; call the TSR, returns the character in A, the TSR resets the flag
;		RET
;
;CONUART:				; read the character from the UART
;		IN	A,(UART)	
;		IF CONDMASKCONIN	
;					; leave as is
;		ELSE
;		AND	127		; mask out ascii 128 to 255 = rubbish that might come from an xmodem fail
;		ENDIF
;		RET			; return
;
;	ENDIF



	
	.IF	CONDCONOUT

CONOUT:				; CONSOLE CHARACTER OUTPUT FROM REGISTER C

CONOUT1:
		IN	A,(UART + 05H)	; READ LINE STATUS REGISTER
		AND	20H		; TEST IF UART IS READY TO SEND
		JP	Z,CONOUT1	; IF NOT REPEAT

		LD	A,C		; GET TO ACCUMULATOR
		OUT	(UART),A	; THEN WRITE THE CHAR TO UART (UART0 = $68 + $00)
		RET
	
	.ELSE

CONOUT:				; CONSOLE CHARACTER OUTPUT FROM REGISTER C
CONOUT1:
		IN	A,(UART + 05H)	; READ LINE STATUS REGISTER
		AND	20H		; TEST IF UART IS READY TO SEND
		JP	Z,CONOUT1	; IF NOT REPEAT
		LD	A,C		; GET TO ACCUMULATOR
		LD	B,A		;  n8VEM 
		CALL	0F2F9H		;  changes B so B<>A if echo off so can do a CP and skip the out
		CP	B		;  still .EQUal after the call
		JR	NZ,ECHOOFF	;  something chandged B so skip the uart out. Try the LCD
		OUT	(UART),A	; THEN WRITE THE CHAR TO UART (UART0 = $68 + $00)
	
ECHOOFF:	CALL	0F2F0H		; TSR1	LCD display
		RET

	.ENDIF



;
LIST:					;LIST CHARACTER FROM REGISTER C
	LD	A,C			;CHARACTER TO REGISTER A
	RET				;NULL SUBROUTINE
;
LISTST:					;RETURN LIST STATUS (0 IF NOT READY, 1 IF READY)
	XOR	A			;0 IS ALWAYS OK TO RETURN
	RET
;
PUNCH:					;PUNCH CHARACTER FROM REGISTER C
	LD	A,C			;CHARACTER TO REGISTER A
	RET				;NULL SUBROUTINE
;
READER:					;READ CHARACTER INTO REGISTER A FROM READER DEVICE
	LD	A,C			;CHARACTER TO REGISTER A
	RET
;
;	I/O DRIVERS FOR THE DISK FOLLOW
;	FOR NOW, WE WILL SIMPLY STORE THE PARAMETERS AWAY FOR USE
;	IN THE READ AND WRITE SUBROUTINES
;

;
;   SELECT DISK GIVEN BY REGISTER C
;
SELDSK:	LD	HL,0000H	; ERROR RETURN CODE
	LD	A,C
	.IF CONDABONLY
		CP	07H		; MUST BE BETWEEN 0 AND 6
	.ELSE
		CP	02H		; if no IDE then only drive A and B for the mini N8VEM so 0 or 1 only
	.ENDIF
	RET	NC		; original code - returns but gets stuck in a loop
	LD	(DISKNO),A

;   DISK NUMBER IS IN THE PROPER RANGE
;   COMPUTE PROPER DISK PARAMETER HEADER ADDRESS
	LD	L,A		; L=DISK NUMBER 0,1,2,3,4
	LD	H,0		; HIGH ORDER ZERO
	ADD	HL,HL		; *2
	ADD	HL,HL		; *4
	ADD	HL,HL		; *8
	ADD	HL,HL		; *16 (SIZE OF EACH HEADER)
	LD	DE,DPBASE
	ADD	HL,DE		; HL=.DPBASE(DISKNO*16)
	RET


;
HOME:				; MOVE TO THE TRACK 00 POSITION OF CURRENT DRIVE
				; TRANSLATE THIS CALL INTO A SETTRK CALL WITH PARAMETER 00
	LD	BC,0		; SELECT TRACK 0000
	

SETTRK:				; SET TRACK GIVEN BY REGISTER BC
	LD	H,B
	LD	L,C
	LD	(TRACK),HL
	RET
;
SETSEC:				; SET SECTOR GIVEN BY REGISTER BC
	LD	H,B
	LD	L,C
	LD	(SECTOR),HL
	RET
;
;   TRANSLATE THE SECTOR GIVEN BY BC USING THE
;   TRANSLATE TABLE GIVEN BY DE
; ONLY USED FOR FLOPPIES! FOR ROMDISK/RAMDISK IT'S 1:1
; DO THE NEXT ROUTINE IS A NULL (RETURNS THE SAME)
SECTRN:	
	LD	H,B
	LD	L,C
	RET
;
SETDMA:				; SET DMA ADDRESS GIVEN BY REGISTERS B AND C
	LD	L,C		; LOW ORDER ADDRESS
	LD	H,B		; HIGH ORDER ADDRESS
	LD	(DMAAD),HL	; SAVE THE ADDRESS
	RET
;________________________________________________________________________________________________________
;  DISK DRIVERS...
;
; DRIVER NEED TO DO SEVERAL THINGS FOR ROM AND RAM DISKS.
;   - INTERRUPTS ARE NOT ALLOWED DURING LOW RAM/ROM ACCESS (DISABLE!)
;   -TRANSLATE TRACK AND SECTOR INTO A POINTER TO WHERE THE 128 BYTE 
;     SECTOR BEGINS IN THE RAM/ROM
;   -TRANSLATE THE DRIVE INTO A RAM/ROM SELECT, COMBINE WITH TRACK ADDRESS
;     AND SEND TO THE MAP PORT.
;   -COPY 128 BYTE FROM OR TO THE ROM/RAMDISK AND MEMORY POINTED TO BY THE DMA 
;     ADDRESS PREVIOUSLY STORED.
;   -RESTORE MAP PORT TO PRIOR CONDITION BEFOR READ/WRITE
;
;   - FIRST TRICK IS THAT WE MADE SECTORS 256 AS 256*128=32768.  SO WE COPY 
;     THE LOW SECTOR ADDRESS TO THE LOW BYTE OF THE HL REGISTER AND THEN 
;     MULTIPLY BY 128. THIS RESULTS IN THE STARTING ADDRESS IN THE RAM OR ROM
;     (0000 -> 7F80H) 32K PAGE.
;
;    - TRICK TWO IS THE TRACK ADDRESS .EQUALS THE 32K PAGE ADDRESS AND IS A 
;      DIRECT SELECT THAT CAN BE COPIED TO THE MAP PORT D0 THROUGH D5.  D7
;      SELECTS THE DRIVE (ROM OR RAM).
;      THAT MEANS THE LOW BYTE OF TRACK CONTAINS THE D0-D5 VALUE AND 
;      DISKNO HAS THE DRIVE SELECTED.  WE FIRST COPY DISKNO TO ACC
;      AND RIGHTSHIFT IT TO PLACE THAT IN BIT 7, WE THEN ADD THE LOW BYTE OF 
;      TRACK TO ACC AND THEN SEND THAT TO THE MAP PORT.
;
;      NOTE 1: A WRITE TO ROM SHOULD BE FLAGGED AS AN ERROR.
;      NOTE 2: RAM MUST START AS A "FORMATTED DISK"  IF BATTERY BACKED UP
;                   IT'S A DO ONCE AT COLD COLD START.  IF NOT BATTERY BACKED U
;                   IT WILL HAVE TO BE DONE EVERY TIME THE SYSTEM IS POWERED.
;                   FORMATTING THE RAM IS SIMPLE AS CLEARING THE DIRECTORY AREA
;                   TO A VALUE OF E5H (THE FIRST 8K OF TRACK 1 OR THE RAMDISK).
;                   IT COULD BE DONE AS A SIMPLE UTILITY PROGRAM STORED IN ROMD
;                   OR ANYTIME COLBOOT IS CALLED(LESS DESIREABLE).
;
;     -WE NOW CAN COPY TO OR FROM AS CORRECT FOR THE DEVICE 128 BYTES (SECTOR)
;      TO OR FROM THE DMA ADDRESS. ALMOST!  SINCE ROM OR RAM IS BEING PAGED
;      WE HAVE TO COPY ANYTHING DETINED FOR BELOW 8000H TO A TEMP BUFFER THEN
;      HANDLE THE PAGING.
;        
;
;     - LAST STEP IS TO RESTORE THE MAP PORT TO POINT TO THE RAM (TRACK 0) SO T
;       MEMORY MAP IS ALL RAM AGAIN AND NOT POINTING INTO THE DATA AREAS OR THE
;       SINCE THE RAM 0TH PAGE IS NOMINALLY THE LOW 32K OF RAM IN THE SYSTEM WE
;       SEND A SIMPLE MVI A,80H ; OUT MPCL_ROM; MVI A,00H ; OUT MPCL_RAM.
;
;      - THE READ OR WRITE OPERATION IS DONE.
;
;   READ DISK
;    USES DE,DL, BC,  ACC FLAGS
;      Z80 COULD USE BLOCK MOVE [LDIR] BUT WRITTEN IN 8080 
;________________________________________________________________________________________________________

;__READ__________________________________________________________________________________________________
;
; 	PERFORM CP/M SECTOR READ
;________________________________________________________________________________________________________
READ:
	DI				; DISABLE INTERRUPTS
	LD	A,(DISKNO)		; GET DRIVE
	.IF CONDSWAPAB			;
	CP	00H			; "A"
	JP	Z,READ_FLOPPY_DISK 	; READ FLOPPY
					; 
	CP	01H			; "B"
	JP	Z,READ_RAM_DISK		; READ FROM 448K RAM DISK
	.ELSE				;
	CP	01H			; "B"
	JP	Z,READ_FLOPPY_DISK 	; READ FLOPPY
					; 
	CP	00H			; "A"
	JP	Z,READ_RAM_DISK		; READ FROM 448K RAM DISK
	.ENDIF				;
	CP	02H			; "C"
	JP	Z,READ_IDE		; READ FROM 8 MB IDE HARD DISK
	CP	03H			; "D"
	JP	Z,READ_ATAPI		; READ FROM 8 MB ATAPI
	CP	04H			; "E"
	JP	Z,READ_HDPART4		; READ FROM 1 MB IDE HARD DISK, PARTITION 4
	CP	05H			; "F"
	JP	Z,READ_RAM_DISK		; READ FROM 1M ROM DISK (UTILIZES SAME
					; ROUTINES AS RAM_DISK
					; "G"
					; READ FROM 22K EEPROM DISK , SO FALL THROUGH

;___READ_EEPROM_DISK_____________________________________________________________________________________
;
;	READ EEPROM DISK
;________________________________________________________________________________________________________
READ_EEPROM_DISK:
					; 
					; IF ROM, MAP TRACK/SECTOR TO VIRTUAL TRACK/SECTOR
					; HANDLE READING FROM ROM HERE
					; 
					; PURPOSE OF THIS ROUTINE IS TO MAP 32K ROM PART
					; TRACK/SECTOR MAP (2K TRACK SIZE MADE OF 16 128
					; BYTE SECTORS EACH) ONTO WHAT THE RAM/ROM SECTOR
					; READ ROUTINES ARE EXPECTING (32K TRACK SIZE MADE
					; OF 256 128 BYTE SECTORS EACH).  THE ROUTINE 
					; CONVERTS 4 BIT TRACK # AND 4 BIT SECTOR #
					; INTO A VIRTUAL 1 TRACK, 256 SECTOR ACCESS
	LD	HL,(TRACK)		; TRACK # IS UPPER 4 BITS OF SECTOR ADDRESS
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*2)
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*4)
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*8)
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*16)
	LD	B,H			; PUT UPPER 4 BITS OF SECTOR ADDRESS IN BC
	LD	C,L			; (B IS UPPER BYTE AND C IS LOWER BYTE)
					; BC NOW CONTAINS THE UPDATED TRACK #
	LD	HL,(SECTOR)		; SECTOR # IS LOWER 4 BITS OF SECTOR ADDRESS
	ADD	HL,BC			; VIRTUAL SECTOR = (UPDATED TRACK #) + SECTOR #
	LD	(V_SECTOR),HL		; STORE VIRTUAL SECTOR #
					; NOW CONTINUE READING ROM WITH REGULAR RAM
					; SETUP FOR READ OF RAM OR ROM DISK
	LD	HL,(V_SECTOR)		;
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*2)
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*4)
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*8)
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*16)
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*32)
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*64)
	ADD	HL,HL			; SHIFT BITS LEFT 1 (*128)
	LD	(SECST),HL		; SAVE SECTOR STARTING ADDRESS
					; SET PAGER WITH DRIVE (0) AND TRACK (0)
	LD	A,00H			; SWITCH IN ROM PAGE 
	OUT	(MPCL_ROM),A		; SEND TO PORT MAPPER
	LD	(PAGER),A		; SAVE COPY (JUST BECAUSE)	
	LD	HL,TMPBUF		; LOAD HL WITH TEMP BUF ADDRESS
	LD	E,L			;
	LD	D,H			; GET IT INTO DE
	LD	HL,(SECST)		; GET ROM/RAM ADDRESS
	CALL	COPY_CPM_SECTOR		;
	CALL	RPAGE			; SET PAGE TO CP/M RAM
	LD	HL,(DMAAD)		; LOAD HL WITH DMA ADDRESS
	LD	E,L			;
	LD	D,H			; GET IT INTO DE
	LD	HL,TMPBUF		; GET ROM/RAM ADDRESS
	CALL	COPY_CPM_SECTOR		;
	LD	A,(DISKNO)		; STORE CURRENT DRIVE IN BUFFER
	LD	(cuDISK),A		;
	LD	A,00H			;
	EI				; RE-ENABLE INTERRUPTS
	RET			

;___READ_RAM_DISK_________________________________________________________________________________________
;
;	READ RAM DISK
;________________________________________________________________________________________________________
READ_RAM_DISK:				;
					; IF RAM, PROCEED WITH NORMAL TRACK/SECTOR READ
	CALL	SECPAGE			; SETUP FOR READ OF RAM OR ROM DISK
	CALL	PAGERB			; SET PAGER WITH DRIVE AND TRACK
	LD	HL,TMPBUF		; LOAD HL WITH TEMP BUF ADDRESS
	LD	E,L			;
	LD	D,H			; GET IT INTO DE
	LD	HL,(SECST)		; GET ROM/RAM ADDRESS
	CALL	COPY_CPM_SECTOR		; MOVE SECTOR TO TMPBUF
	CALL	RPAGE			; SET PAGE TO CP/M RAM
	LD	HL,(DMAAD)		; LOAD HL WITH DMA ADDRESS					;
	LD	E,L			;
	LD	D,H			; GET IT INTO DE
	LD	HL,TMPBUF		; GET ROM/RAM ADDRESS
	CALL	COPY_CPM_SECTOR		; MOVE SECTOR FROM TMPBUF TO DMA AREA
	LD	A,(DISKNO)		; STORE CURRENT DRIVE IN BUFFER
	LD	(cuDISK),A		;
	LD	A,00H			;
	EI				; RE-ENABLE INTERRUPTS
	RET
	
;___TRFLSEC______________________________________________________________________________________________
;
;	TRANSLATE LOGICAL FLOPPY DISK SECTOR TO PHYSICAL SECTOR
;	IN:  TRACK,SECTOR
;	OUT: pTRACK,pSECTOR,SECTOR_INDEX
;________________________________________________________________________________________________________
TRFLSEC:	
	LD	A,(TRACK)		; LOAD TRACK # (LOW BYTE)
	AND	01H			; FILTER OUT HEAD
	LD	(HEAD),A		; STORE HEAD
	LD	A,(TRACK)		; SAVE TRACK IN A
	SRL	A			; REMOVE HEAD BIT	
	LD	(pTRACK),A		; STORE IN TRACK
	LD	A,(SECTOR)		; LOAD SECTOR # (LOW BYTE)
	LD	(SECTOR_INDEX),A	; STORE SECTOR IN SECTOR INDEX
	SRL	A			; 
	SRL	A			; DIVIDE BY 4 (FOR BLOCKING)
	LD	(pSECTOR),A		; STORE IN SECTOR
	LD	A,(SECTOR_INDEX)	; FILTER OUT UNWANTED BITS
	AND	03H			;
	LD	(SECTOR_INDEX),A	;
	RET	

;___DEBSEC_______________________________________________________________________________________________
;
;	DEBLOCK 512 BYTE SECTOR FOR CP/M
;
;________________________________________________________________________________________________________
DEBSEC:		
	LD	HL,(DMAAD)		; LOAD HL WITH DMA ADDRESS
	LD	D,H			; TRANSFER HL REGISTERS TO DE
	LD	E,L			;
	PUSH	DE			; STORE DE
					; COMPUTE STARTING ADDRESS OF CP/M SECTOR IN READ SECTOR BUFFER
	LD	HL,SECTOR_BUFFER 	; LOAD HL WITH SECTOR BUFFER ADDRESS
	LD	A,(SECTOR_INDEX) 	; GET THE SECTOR INDEX (CP/M SECTOR OFFSET IN BUFFER)
	RRCA				; MOVE BIT 0 TO BIT 7
	RRCA				; DO AGAIN - IN EFFECT MULTIPLY BY 4
	LD	D,00H			; PUT RESULT AS 16 VALUE IN DE, UPPER BYTE IN D IS $00
	LD	E,A			; PUT ADDRESS OFFSET IN E
	ADD	HL,DE			; MULTIPLY BY 2, TOTAL MULTIPLICATION IS X 128
	ADD	HL,DE			; CP/M SECTOR STARTING ADDRESS IN IDE HD SECTOR BUFFER
					; COPY CP/M SECTOR TO BDOS DMA ADDRESS BUFFER					
	POP	DE			; RESTORE DE					
	CALL	COPY_CPM_SECTOR		;
	RET

;___BLKSEC_______________________________________________________________________________________________
;
;	BLOCK 512 BYTE SECTOR FOR CP/M
;
;________________________________________________________________________________________________________
BLKSEC:	
					; COMPUTE STARTING ADDRESS OF CP/M SECTOR IN READ SECTOR BUFFER	
	LD	HL,SECTOR_BUFFER 	; LOAD HL WITH SECTOR BUFFER ADDRESS
	LD	A,(SECTOR_INDEX) 	; GET THE SECTOR INDEX (CP/M SECTOR OFFSET IN BUFFER)
	RRCA				; MOVE BIT 0 TO BIT 7
	RRCA				; DO AGAIN - IN EFFECT MULTIPLY BY 64
	LD	D,00			; PUT RESULT AS 16 VALUE IN DE, UPPER BYTE IN D IS $00
	LD	E,A			; PUT ADDRESS OFFSET IN E
	ADD	HL,DE			; CP/M SECTOR STARTING ADDRESS IN IDE HD SECTOR BUFFER
	ADD	HL,DE			; MULTIPLY BY 2, TOTAL MULTIPLICATION IS X 128
	LD	(SECST),HL		; KEEP CP/M SECTOR ADDRESS FOR LATER USE
					; COPY CP/M SECTOR FROM BDOS DMA ADDRESS BUFFER
	LD	HL,(SECST)		; LOAD CP/M SECTOR ADDRESS (WHERE THE DATA IS TO BE WRITTEN)
	LD	D,H			; TRANSFER HL REGISTERS TO DE
	LD	E,L			;
	LD	HL,(DMAAD)		; LOAD HL WITH DMA ADDRESS (WHERE THE DATA TO BE WRITTEN IS)
	CALL	COPY_CPM_SECTOR		;
	RET
	
;___ISCUR_______________________________________________________________________________________________
;
;	IS CURRENT SECTOR IN BUFFER?
;
;________________________________________________________________________________________________________
ISCUR:				
	LD	HL,(pSECTOR)		; COMPARE REQUESTED SECTOR WITH SECTOR IN BUFFER
	LD	A,(cuSECTOR)		; 
	CP	L			;
	RET	NZ			; LOW BYTE NOT .EQUAL
	LD	A,(cuSECTOR+1)		; 
	CP	H			;
	RET	NZ			; HIGH BYTE NOT .EQUAL
	LD	HL,(pTRACK)		; COMPARE REQUESTED TRACK WITH TRACK IN BUFFER
	LD	A,(cuTRACK)		; 
	CP	L			; LOW BYTE NOT .EQUAL
	RET	NZ			;
	LD	A,(cuTRACK+1)		; 
	CP	H			;
	RET	NZ			; HIGH BYTE NOT .EQUAL
	LD	HL,(DISKNO)		; COMPARE REQUESTED DRIVE WITH DRIVE IN BUFFER
	LD	A,(cuDISK)		;
	CP	L			;
	RET				; EXIT WITH RESULT

;___READ_FLOPPY_DISK_____________________________________________________________________________________
;
;	READ FLOPPY DISK
;
;________________________________________________________________________________________________________

READ_FLOPPY_DISK:
	DI				; DISABLE INTERRUPTS
	LD	HL,0			;
	ADD	HL,SP			; GET STACK POINTER INTO HL
	LD	(PARKSTACK),HL		; SAVE STACK POINTER.
	LD	SP,FLOPPYSTACK		; 
	CALL	TRFLSEC			; TRANSLATE SECTOR INFORMATION
	CALL 	READ_FLOPPY_SECTOR	;
	CALL	DEBSEC			; DEBLOCK SECTOR
	LD	A,(ST1)			; LOAD RESULT CODE INTO A
READ_FLOPPY_DISK_EXIT:			;
	LD	SP,(PARKSTACK)		; RETURN STACK
	EI				; RE-ENABLE INTERRUPTS
	RET
	
	
;___READ_FLOPPY_SECTOR_____________________________________________________________________________________
;
;	READ A SECTOR FROM A FLOPPY DISK
;
;________________________________________________________________________________________________________
READ_FLOPPY_SECTOR:
	LD	A,0			; RESET STATUS FLAG 1
	LD	(ST1),A			;
	CALL	ISCUR			; IS CURRENT SECTOR ALREADY IN BUFFER
	JP	Z,READ_FLOPPY_SECTOR_OK	;
	LD	A,20			; 20 RETRIES
	LD	(RETRY),A		;
	LD	A,2			; 2 ITERATIONS OF RETRIES 
	LD	(RETRY1),A		;	
READ_FLOPPY_SECTOR_RETRY:		;
	CALL	FLOPPYREAD		; READ THE FLOPPY DISK SECTOR
	LD	A,(ST0)			; GET STATUS FLAG 0
	AND	0F8H			; MASK OF DRIVE AND HEAD SELECTION
	LD	B,A			; MOVE STATUS FLAG 0 TO B
	LD	A,(ST1)			; GET STATUS FLAG 1
	OR	B			; IF ZERO READ WAS OK
	JP	Z,READ_FLOPPY_SECTOR_OK
	LD	A,(RETRY)		; READ NOT OK, DEC RETRY COUNTER
	DEC	A			; 
	LD	(RETRY),A		; STORE NEW RETRY COUNTER
	JP	NZ,READ_FLOPPY_SECTOR_RETRY
	CALL	CYCLEFLOPPY		; Cycle floppy head
	LD	A,20			; RESET TO 20 RETRIES
	LD	(RETRY),A		; STORE RETRY COUNTER
	LD	A,(RETRY1)		; DEC RETRY ITERATION COUNTER
	DEC	A			;
	LD	(RETRY1),A		;
	JP	NZ,READ_FLOPPY_SECTOR_RETRY
	LD	HL,0FFFFH		; SET INVALID CONDITION, BUFFER IS INVALID
	LD	(cuSECTOR),HL		; CURRENT PHYSICAL DISK SECTOR IN BUFFER
	LD	(cuTRACK),HL		; CURRENT PHYSICAL DISK TRACK IN BUFFER
	RET				;
READ_FLOPPY_SECTOR_OK:			;
	LD	HL,(pSECTOR)		; STORE PHYSICAL SECTOR IN BUFFER
	LD	(cuSECTOR),HL		; 
	LD	HL,(pTRACK)		; STORE PHYSICAL DISK TRACK IN BUFFER
	LD	(cuTRACK),HL		; 
	LD	A,(DISKNO)		; STORE CURRENT DRIVE IN BUFFER
	LD	(cuDISK),A		;
	RET
	
;___READ_IDE_____________________________________________________________________________________________
;
;	READ  FROM IDE HARD DISK  
;________________________________________________________________________________________________________

READ_IDE:
	DI				; DISABLE INTERRUPTS
	LD	HL,0FC00H		; INITIALIZE LBA OFFSET SECTOR LO WORD
	LD	(LBA_OFFSET_LO),HL	;
	LD	HL,0000H		; INITIALIZE LBA OFFSET SECTOR HI WORD
	LD	(LBA_OFFSET_HI),HL	;
	CALL	CONVERT_IDE_SECTOR_CPM 	; COMPUTE WHERE THE CP/M SECTOR IS ON THE
	CALL	IDE_READ_SECTOR		; READ THE IDE HARD DISK SECTOR
	JP	NC,READ_IDE_ERROR	;
	CALL	DEBSEC			;
	EI				; RE-ENABLE INTERRUPTS
	LD	A,00H			; RETURN ERROR CODE READ SUCCESSFUL A=0
	RET				;
READ_IDE_ERROR:				;
	EI				; RE-ENABLE INTERRUPTS
	LD	A,0FFH			; RETURN ERROR CODE READ ERROR A=FF
	RET	

;___READ_ATAPI_________________________________________________________________________________________
;
;	READ  FROM ATAPI DEVICE
;________________________________________________________________________________________________________	
READ_ATAPI:
	LD	A,10H			; SET TO SECONDARY DEVICE
	LD	(IDEDEVICE),A		;
	DI				; DISABLE INTERRUPTS
	LD	HL,0000H		; INITIALIZE LBA OFFSET SECTOR LO WORD
	LD	(LBA_OFFSET_LO),HL	;
	LD	HL,0000H		; INITIALIZE LBA OFFSET SECTOR HI WORD
	LD	(LBA_OFFSET_HI),HL	;
	CALL	CONVERT_IDE_SECTOR_CPM	; COMPUTE WHERE THE CP/M SECTOR IS ON THE
	CALL	ATAPI_READ_SECTOR	; READ THE IDE HARD DISK SECTOR
	JP	NC,READ_ATAPI_ERROR	;
	CALL	DEBSEC					;									;
	EI				; RE-ENABLE INTERRUPTS
	LD	A,00H			; RETURN ERROR CODE READ SUCCESSFUL A=0
	RET				;
READ_ATAPI_ERROR:			;
	EI				; RE-ENABLE INTERRUPTS
	LD	A,0FFH			; RETURN ERROR CODE READ ERROR A=FF
	RET				;
		
READ_HDPART4:
					; STUB
	RET

;___WRITE______________________________________________________________________________________________
;
;   HANDLE CP/M WRITE CALL
;
;________________________________________________________________________________________________________
WRITE:
	DI				; DISABLE INTERRUPTS
	LD	A,(DISKNO)		; GET DRIVE
	.IF CONDSWAPAB			;
	CP	00H			; FIND OUT WHICH DRIVE IS BEING REQUESTED
	JP	Z,WRITE_FLOPPY_DISK	;
	CP	01H			; 
	JP	Z,WRITE_RAM_DISK 	; WRITE TO 448K RAM DISK
	.ELSE				;
	CP	01H			; FIND OUT WHICH DRIVE IS BEING REQUESTED
	JP	Z,WRITE_FLOPPY_DISK	;				
	CP	00H			; 
	JP	Z,WRITE_RAM_DISK 	; WRITE TO 448K RAM DISK
	.ENDIF				;
	CP	02H			;
	JP	Z,WRITE_IDE		; WRITE TO 8 MB IDE HARD DISK, PARTITION 2
	CP	03H			;
	JP	Z,WRITE_ATAPI		; WRITE TO 8 MB IDE HARD DISK, PARTITION 3
	CP	04H			;
	JP	Z,WRITE_HDPART4		; WRITE TO 1 MB IDE HARD DISK, PARTITION 4


;___RDONLY______________________________________________________________________________________________
;
;   HANDLE WRITE TO READ ONLY
;
;   SENDS A MESSAGE TO TERMINAL THAT ROM DRIVE IS NOT WRITEABLE
;   DOES A PAUSE THEN RETURNS TO CPM WITH ERROR FLAGGED. THIS IS
;   DONE TO ALLOW A POSSIBLE GRACEFUL EXIT (SOME APPS MAY PUKE).
;________________________________________________________________________________________________________
RDONLY:
	LD	HL,TXT_RO_ERROR		; SET HL TO START OF ERROR MESSAGE
	CALL	PRTMSG			; PRINT ERROR MESSAGE
	LD	A,01H			; SEND BAD SECTOR ERROR BACK
					; BDOS WILL ALSO PRINT ITS OWN ERROR MESSAGE
	RET

;___WRITE_RAM_DISK_____________________________________________________________________________________
;
;	WRITE RAM DISK
;________________________________________________________________________________________________________
WRITE_RAM_DISK:
	LD	HL,TMPBUF		; LOAD HL WITH TEMP BUF ADDRESS
	LD	E,L			;
	LD	D,H			; GET IT INTO DE
	LD	HL,(DMAAD)		; GET DMA ADDRESS
	CALL	COPY_CPM_SECTOR		;
	CALL	SECPAGE			; GET RAM PAGE WRITE ADDRESS
	CALL	PAGERB			; SET PAGER WITH DRIVE AND TRACK
	LD	HL,(SECST)		; LOAD HL WITH DMA ADDRESS (WHERE TO WRITE TO)
	LD	E,L			;
	LD	D,H			; GET IT INTO DE
	LD	HL,TMPBUF		; GET TEMP BUFFER ADDRESS
	CALL	COPY_CPM_SECTOR		;
	CALL	RPAGE			; SET BACK TO RAM
	LD	A,(DISKNO)		; STORE CURRENT DRIVE IN BUFFER
	LD	(cuDISK),A		;
	LD	A,00H			;
	EI				; RE-ENABLE INTERRUPTS
	RET
	
;___WRITE_FLOPPY_DISK_____________________________________________________________________________________
;
;	WRITE FLOPPY DISK
;________________________________________________________________________________________________________
WRITE_FLOPPY_DISK:
	DI				; DISABLE INTERRUPTS
	LD	HL,0			;
	ADD	HL,SP			; MOVE STACK POINTER TO HL
	LD	(PARKSTACK),HL		; SAVE STACK POINTER.
	LD	SP,FLOPPYSTACK		;
	CALL	TRFLSEC			; TRANSLATE SECTOR INFORMATION
	CALL 	READ_FLOPPY_SECTOR	;
	LD	A,(ST1)			; GET STATUS CODE
	JP	NZ,WRITE_FLOPPY_DISK_OK	;
WRITE_READ_FLOPPY_DISK_OK:		;
	CALL	BLKSEC			; BLOCK SECTOR 
					; IDE HD SECTOR IS NOW UPDATED WITH CURRENT CP/M SECTOR DATA SO WRITE TO DISK
	LD	A,20			; 20 RETRIES
	LD	(RETRY),A		;
WRITE_FLOPPY_DISK_RETRY:		;
	CALL	FLOPPYWRITE		; WRITE THE FLOPPY DISK SECTOR
	LD	A,(ST0)			; GET STATUS CODE 0
	AND	0F8H			; MASK OF DRIVE AND HEAD SELECTION
	LD	B,A			; MOVE STATUS CODE 0 TO B
	LD	A,(ST1)			; GET STATUS CODE 1
	OR	B			; IF ZERO WRITE WAS OK
	JP	Z,WRITE_FLOPPY_DISK_OK
	LD	A,(RETRY)		; BAD WRITE, DEC RETRY COUNTER
	DEC	A			;
	LD	(RETRY),A		; STORE NEW RETRY COUNTER
	JP	NZ,WRITE_FLOPPY_DISK_RETRY
WRITE_FLOPPY_DISK_OK:			;
	LD	SP,(PARKSTACK)		; RESTORE STACK
	LD	A,(ST1)			; GET STATUS CODE 1
	EI				; RE-ENABLE INTERRUPTS
	RET

	

;___WRITE_IDE____________________________________________________________________________________________
;
;	WRITE TO IDE DEVICE
;________________________________________________________________________________________________________		
WRITE_IDE:
	DI				; DISABLE INTERRUPTS
	LD	HL,0FC00H		; INITIALIZE LBA OFFSET SECTOR LO WORD
	LD	(LBA_OFFSET_LO),HL	;
	LD	HL,0000H		; INITIALIZE LBA OFFSET SECTOR HI WORD
	LD	(LBA_OFFSET_HI),HL	;
					;
	CALL	CONVERT_IDE_SECTOR_CPM 	; COMPUTE WHERE THE CP/M SECTOR IS ON THE
	CALL	IDE_READ_SECTOR		; READ THE IDE HARD DISK SECTOR
	JP	NC,WRITE_IDE_ERROR	; ON ERROR, ABORT
	CALL	BLKSEC			; DEBLOCK SECTOR
	CALL	IDE_WRITE_SECTOR 	; WRITE THE UPDATED IDE HARD DISK SECTOR
	EI				; RE-ENABLE INTERRUPTS
	LD	A,00H			; RETURN ERROR CODE WRITE SUCCESSFUL A=0
	RET				;
WRITE_IDE_ERROR:			;
	EI				; RE-ENABLE INTERRUPTS
	LD	A,0FFH			; RETURN ERROR CODE WRITE ERROR A=FF
	RET

;___WRITE_ATAPI__________________________________________________________________________________________
;
;	WRITE TO ATAPI DEVICE
;________________________________________________________________________________________________________		
WRITE_ATAPI:
	LD	A,10H			; SET TO SECONDARY DEVICE
	LD	(IDEDEVICE),A		;
	DI				; DISABLE INTERRUPTS
					;
	LD	HL,0000H		; INITIALIZE LBA OFFSET SECTOR LO WORD
	LD	(LBA_OFFSET_LO),HL	;		
					;
	LD	HL,0000H		; INITIALIZE LBA OFFSET SECTOR HI WORD
	LD	(LBA_OFFSET_HI),HL	;	
					;
	CALL	CONVERT_IDE_SECTOR_CPM	; COMPUTE WHERE THE CP/M SECTOR IS ON THE DISK
					;
	CALL	ATAPI_READ_SECTOR	; READ SECTOR
	JP	NC,WRITE_ATAPI_ERROR	; ON ERROR, ABORT
	CALL	BLKSEC			; DEBLOCK SECTOR
	CALL	ATAPI_WRITE_SECTOR	;
					;
	EI				; RE-ENABLE INTERRUPTS
	LD	A,00H			; RETURN ERROR CODE WRITE SUCCESSFUL A=0
	RET				;
WRITE_ATAPI_ERROR:			;
	EI				; RE-ENABLE INTERRUPTS
	LD	A,0FFH			; RETURN ERROR CODE WRITE ERROR A=FF
	RET
	
WRITE_HDPART4:
					; STUB
	RET


;___PRTMSG_______________________________________________________________________________________________
;
;	PRINT MESSAGE POINTED TO BY HL ON CONSOLE DEVICE
;________________________________________________________________________________________________________		
PRTMSG:
	LD	A,(HL)			; GET CHARACTER TO A
	CP	END			; TEST FOR END BYTE
	JP	Z,PRTMSG1		; JUMP IF END BYTE IS FOUND
	LD	C,A			; PUT CHAR TO PRINT VALUE IN REG C FOR CONOUT
	CALL	CONOUT			; SEND CHARACTER TO CONSOLE FROM REG C
	INC	HL			; INC POINTER, TO NEXT CHAR
	JP	PRTMSG			; TRANSMIT LOOP
PRTMSG1:
	RET


;___SECPAGE_______________________________________________________________________________________________
;
;	UTILITY ROUTINE FOR SECTOR TO PAGE ADDRESS
;________________________________________________________________________________________________________		
SECPAGE:
	LD	HL,(SECTOR)		; GET SECTOR INTO HL
	ADD	HL,HL			; SHIFT BITS 1 TO LEFT (*2)
	ADD	HL,HL			; SHIFT BITS 1 TO LEFT (*4)
	ADD	HL,HL			; SHIFT BITS 1 TO LEFT (*8)
	ADD	HL,HL			; SHIFT BITS 1 TO LEFT (*16)
	ADD	HL,HL			; SHIFT BITS 1 TO LEFT (*32)
	ADD	HL,HL			; SHIFT BITS 1 TO LEFT (*64)
	ADD	HL,HL			; SHIFT BITS 1 TO LEFT (*128)
	LD	(SECST),HL		; SAVE SECTOR STARTING ADDRESS
	RET
	
;___PAGERB_______________________________________________________________________________________________
;
;	PAGER BYTE CREATION
;    	ASSEMBLES DRIVE AND TRACK AND SENDS IT TO PAGER PORT
;________________________________________________________________________________________________________		
PAGERB:	
	LD	HL,(TRACK)		; LOAD TRACK INTO HL
	LD	A,(DISKNO)		; LOAD DISK INTO A
	CP	05H			; IS ROM?
	JP	Z,ROMD			; READ FROM 1M ROM DISK
	CP	06H			; IS ROM?
	JP	Z,ROMD			; READ FROM 22K ROM DISK
	AND	1			; MASK FOR 1 BIT OF DRIVE SELECT 
	RRCA				; MOVE BIT 0 TO BIT 7
	OR	L			; OR L WITH ACC TO COMBINE TRACK AND DRIVE
	OUT	(MPCL_RAM),A		; SEND TO RAM PORT MAPPER
	LD	(PAGER),A		; SAVE COPY (JUST BECAUSE)
	RET				;
ROMD:					;
	LD	A,05h			;
	AND	1			; MASK FOR 1 BIT OF DRIVE SELECT 
	RRCA				; MOVE BIT 0 TO BIT 7
	OR	L			; OR L WITH ACC TO COMBINE TRACK AND DRIVE
	AND	7FH			; STRIP OFF BIT 7 (ROM_ENABLE BIT)
	OUT	(MPCL_ROM),A		; SEND TO ROM PORT MAPPER
	LD	(PAGER),A		; SAVE COPY (JUST BECAUSE)
	LD	(DB_PAGER),A		; SAVE COPY (JUST BECAUSE) (debug)
	RET

;___RPAGE_______________________________________________________________________________________________
;
;	RESET PAGER BACK TO RAM.  
;________________________________________________________________________________________________________		
RPAGE:
	LD	A,80H			; DESELECT ROM PAGE
	OUT	(MPCL_ROM),A		; SELECT RAM
	LD	A,00H			; SET TO RAM, TRACK 0
	OUT	(MPCL_RAM),A		; SELECT RAM
	LD	(PAGER),A		; SAVE COPY OF PAGER BYTE
	RET

;___COPY_CPM_SECTOR______________________________________________________________________________________
;
; 	COPIES ONE CPM SECTOR FROM ONE MEMORY ADDRESS TO ANOTHER
;	INPUT
;  		 DE SOURCE ADDRESS
;		 HL TARGET ADDRESS
; 	USES C REGISTER
;________________________________________________________________________________________________________		
COPY_CPM_SECTOR:
	LD	BC,128			; BC IS COUNTER FOR FIXED SIZE TRANSFER (128 BYTES)
	LDIR				; TRANSFER
	RET

;___CONVERT_IDE_SECTOR_CPM________________________________________________________________________________
;
; COMPUTES WHERE THE CP/M SECTOR IS IN THE IDE PARTITION
; IDE HD SECTORS ARE 512 BYTES EACH, CP/M SECTORS ARE 128 BYTES EACH
; MAXIMUM SIZE OF CP/M DISK IS 8 MB = 65536 (16 BITS) X 128 BYTES PER SECTOR
; IDE HD PARTITION CAN HAVE AT MOST 16384 IDE SECTORS -> 65536 CP/M SECTORS
; EACH IDE HD SECTOR CONTAINS 4 ADJACENT CP/M SECTORS
; 
; 
; INPUT:
; IDE HD PARTITION STARTING SECTOR NUMBER (FROM PARTITION TABLE)
;  - LOWER 16 BITS STORED IN LBA_OFFSET_LO
;  - UPPER 16 BITS STORED IN LBA_OFFSET_HI
; PARTITION OFFSET IN HL (16 BITS)
;  - A UNIQUELY COMPUTED FUNCTION BASED ON GEOMETRY OF DISKS NUMBER OF
;    CP/M TRACKS AND SECTORS SPECIFIED IN DPB
; 
; 
; OUTPUT:
; IDE TARGET SECTOR (SENT TO IDE HD CONTROLLER FOR READ OPERATION)
;  - LOWER 16 BITS STORED IN LBA_TARGET_LO
;  - UPPER 16 BITS STORED IN LBA_TARGET_HI
; CP/M TO IDE HD SECTOR MAPPING PARAMETER STORED IN SECTOR_INDEX
;  - 8 BIT VALUE WITH 4 LEGAL STATES (00, 01, 02, 04) WHICH IS
;    TO BE USED TO COMPUTE STARTING ADDRESS OF 128 BYTE CP/M SECTOR ONCE
;    512 BYTE IDE HD SECTOR READ INTO MEMORY BUFFER
; 
; ROTATE WITH CARRY 16 BIT TRACK,SECTOR VALUE IN HL TO GET 14 BIT IDE HD
; TARGET SECTOR IN PARTITION
; KEEP LAST TWO BITS IN B FOR IDE HD SECTOR TO CP/M SECTOR TRANSLATION
; COMPUTE SECTOR_INDEX 
;________________________________________________________________________________________________________		
CONVERT_IDE_SECTOR_CPM:

	LD	HL,(TRACK)		; LOAD TRACK # (WORD)
	LD	B,L			; SAVE LOWER 8 BITS (TRACK # 0-255)
	LD	HL,(SECTOR)		; LOAD SECTOR # (WORD)
	LD	H,B			; HL IS 8 BIT TRACK IN H, 8 BIT SECTOR IN L
	XOR	A			; ZERO ACCUMULATOR
	LD	A,L			; STORE LAST 2 BITS OF L IN B
	AND	%00000011		; 
	LD	B,A			;
	LD	(SECTOR_INDEX),A 	; LOCATES WHERE THE 128 BYTE CP/M SECTOR
					; IS WITHIN THE 512 BYTE IDE HD SECTOR
					; COMPUTE WHICH IDE HD SECTOR TO READ TO WITHIN 4 CP/M SECTORS 
					; SHIFTS 16 BIT PARTITION OFFSET TO THE RIGHT 2 BITS AND ADDS RESULT TO
					; IDE HD PARTITION STARTING SECTOR		
					; SHIFT PARTITION OFFSET RIGHT 1 BIT
	SCF				;
	CCF				; CLEAR CARRY FLAG
	LD	A,H			; 16 BIT ROTATE HL WITH CARRY
	RRA				;
	LD	H,A			; ROTATE HL RIGHT 1 BIT (DIVIDE BY 2)
	LD	A,L			;
	RRA				;
	LD	L,A			;		
	SCF				; SHIFT PARTITION OFFSET RIGHT 1 BIT
	CCF				; CLEAR CARRY FLAG
	LD	A,H			; 16 BIT ROTATE HL WITH CARRY
	RRA				;
	LD	H,A			; ROTATE HL RIGHT 1 BIT (DIVIDE BY 2)
	LD	A,L			;
	RRA				;
	LD	L,A			;
					; ADD RESULTING 14 BIT VALUE TO IDE HD PARTITION STARTING SECTOR
					; STORE RESULT IN IDE HD TARGET SECTOR PARAMETER
	LD	A,(LBA_OFFSET_LO) 	; 16 BIT ADD OF LBA_OFFSET_LO WITH HL
	ADD	A,L			;
	LD	(LBA_TARGET_LO),A	;
	LD	A,(LBA_OFFSET_LO+1)	;
	ADC	A,H			;
	LD	(pSECTOR),HL		;
	LD	(LBA_TARGET_LO+1),A 	; STORE OVERFLOW BIT IN CARRY
	LD	HL,0000H		;
	LD	A,(LBA_OFFSET_HI) 	; 16 BIT ADD WITH CARRY OF LBA_OFFSET_HI WITH $0000
	ADC	A,L			;
	LD	(LBA_TARGET_HI),A	;
	LD	A,(LBA_OFFSET_HI+1)	;
	ADC	A,H			;
	LD	(pTRACK),HL		;
	LD	(LBA_TARGET_HI+1),A	;
	RET

;___IDE_READ_SECTOR______________________________________________________________________________________
;
;	READ IDE SECTOR
;________________________________________________________________________________________________________			
IDE_READ_SECTOR:
	CALL	ISCUR			; IS CURRENT SECTOR IN BUFFER?	
	JP	Z,IDE_READ_SECTOR_OK	;
	CALL	IDE_WAIT_BUSY_READY 	; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC			; ERROR, RETURN
	CALL	IDE_SETUP_LBA		; TELL DRIVE WHAT SECTOR IS REQUIRED
	LD	A,20H			;
	OUT	(IDESTTS),A		; $20 = IDE 'READ SECTOR' COMMAND 
IDE_SREX:				;
	CALL	IDE_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC			; ERROR, RETURN
	CALL	IDE_TEST_ERROR		; ENSURE NO ERROR WAS REPORTED
	RET	NC			; ERROR, RETURN
	CALL	IDE_WAIT_BUFFER		; WAIT FOR FULL BUFFER SIGNAL FROM DRIVE
	RET	NC			; ERROR, RETURN
	CALL	IDE_READ_BUFFER		; GRAB THE 256 WORDS FROM THE BUFFER
	SCF				; CARRY = 1 ON RETURN = OPERATION OK
IDE_READ_SECTOR_OK:			;
	LD	HL,(pSECTOR)		; STORE PHYSICAL SECTOR IN BUFFER
	LD	(cuSECTOR),HL		; 
	LD	HL,(pTRACK)		; STORE PHYSICAL DISK TRACK IN BUFFER
	LD	(cuTRACK),HL		; 
	LD	A,(DISKNO)		; STORE CURRENT DRIVE IN BUFFER
	LD	(cuDISK),A		;
	SCF				; CARRY = 1 ON RETURN = OPERATION OK
	RET
	
;___IDE_WRITE_SECTOR_____________________________________________________________________________________
;
;	WRITE IDE SECTOR
;________________________________________________________________________________________________________			
IDE_WRITE_SECTOR:
	CALL	IDE_WAIT_BUSY_READY 	; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC			; ERROR, RETURN
	CALL	IDE_SETUP_LBA		; TELL DRIVE WHAT SECTOR IS REQUIRED
	LD	A,30H			;
	OUT	(IDESTTS),A		; $30 = IDE 'WRITE SECTOR' COMMAND 
	CALL	IDE_WAIT_BUSY_READY	;
	RET	NC			; ERROR, RETURN
	CALL	IDE_TEST_ERROR		; ENSURE NO ERROR WAS REPORTED
	RET	NC			; ERROR, RETURN
	CALL	IDE_WAIT_BUFFER		; WAIT FOR BUFFER READY SIGNAL FROM DRIVE
	RET	NC			; ERROR, RETURN
	CALL	IDE_WRITE_BUFFER 	; SEND 256 WORDS TO DRIVE'S BUFFER
	CALL	IDE_WAIT_BUSY_READY 	; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC			; ERROR, RETURN
	CALL	IDE_TEST_ERROR		; ENSURE NO ERROR WAS REPORTED
	RET	NC			; ERROR, RETURN
	SCF				; CARRY = 1 ON RETURN = OPERATION OK
	RET		

;___IDE_SOFT_RESET_______________________________________________________________________________________
;
;	RESET IDE CHANNEL
;________________________________________________________________________________________________________			
IDE_SOFT_RESET:
	.IF 	CONDIDESOFT		;
	LD	A,%00000110		; NO INTERRUPTS, RESET DRIVE = 1
	OUT	(IDECTRL),A		;
	LD	A,%00000010		; NO INTERRUPTS, RESET DRIVE = 0
	OUT	(IDECTRL),A		;
	CALL	IDE_WAIT_BUSY_READY	;this takes a couple of seconds
	RET	
	.ELSE
	; skip this if no ide drive which speeds up reboots and startups
	RET
	.ENDIF

;___ATAPI_WAIT_BUSY_READY________________________________________________________________________________
;
;	WAIT FOR ATAPI CHANNEL TO BE READY
;________________________________________________________________________________________________________			
ATAPI_WAIT_BUSY_READY:
	LD	DE,0			; CLEAR OUT DE
ATAPI_WBSY:				;
	LD	B,5			; SETUP TIMEOUT
ATAPI_DLP:				;
	DJNZ	ATAPI_DLP		; 
	INC	DE			;
	LD	A,D			;
	OR	E			;
	JR	Z,ATAPI_TO		;
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%10000000		; MASK OFF BUSY BIT
	JR	NZ,ATAPI_WBSY		; WE WANT BUSY(7) TO BE 0 
	SCF				; CARRY 1 = OK
	RET				;
ATAPI_TO:				;
	XOR	A			; CARRY 0 = TIMED OUT
	RET				;
	
;___IDE_WAIT_DRQ_READY___________________________________________________________________________________
;
;	WAIT FOR IDE CHANNEL TO BE READY
;________________________________________________________________________________________________________			
IDE_WAIT_DRQ_READY:
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%00001000		; MASK OFF RDY BIT
	JR	Z,IDE_WAIT_DRQ_READY	; WE WANT DRQ(3) TO BE 1
	RET

;___IDE_WAIT_DRQ_ZERO____________________________________________________________________________________
;
;	WAIT FOR IDE DRQ TO BE ZERO
;________________________________________________________________________________________________________			
IDE_WAIT_DRQ_ZERO:
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%00001000		; MASK OFF RDY BIT
	JR	NZ,IDE_WAIT_DRQ_ZERO	; WE WANT DRQ(3) TO BE 0
	RET

;___IDE_WAIT_BUSY_READY___________________________________________________________________________________
;
;	WAIT FOR IDE CHANNEL TO BE READY
;________________________________________________________________________________________________________			
IDE_WAIT_BUSY_READY:
	LD	DE,0			; CLEAR DE
IDE_WBSY:				;
	LD	B,5			; SETUP TIMEOUT
IDE_DLP:				;
	DEC	B			;
	JP	NZ,IDE_DLP		;
	INC	DE			;
	LD	A,D			;
	OR	E			;
	JP	Z,IDE_TO		;
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%11000000		; MASK OFF BUSY AND RDY BITS
	XOR	%01000000		; WE WANT BUSY(7) TO BE 0 AND RDY(6) TO BE 1
	JP	NZ,IDE_WBSY		;
	SCF				; CARRY 1 = OK
	RET
IDE_TO:
	XOR	A			; CARRY 0 = TIMED OUT
	RET
	
;___IDE_TEST_ERROR_______________________________________________________________________________________
;
;	CHECK FOR IDE ERROR CONDITION
;________________________________________________________________________________________________________			
IDE_TEST_ERROR:
	SCF				;
	IN	A,(IDESTTS)		;
	LD	B,A			; 
	AND	%00000001		; TEST ERROR BIT
	SCF				; 
	RET	Z			;
	LD	A,B			; 
	AND	%00100000		;
	SCF				;
	JP	NZ,IDE_ERR		; TEST WRITE ERROR BIT
	IN	A,(IDEERR)		; READ ERROR FLAGS
IDE_ERR:
	OR	A			; CARRY 0 = ERROR
	RET				; IF A = 0, IDE BUSY TIMED OUT

;___IDE_WAIT_BUFFER_______________________________________________________________________________________
;
;	WAIT FOR DATA BUFFER READY
;________________________________________________________________________________________________________			
IDE_WAIT_BUFFER:
	LD	DE,0			;
IDE_WDRQ:				;
	LD	B,5			;
IDE_BLP:				;
	DEC	B			;
	JP	NZ,IDE_BLP		;	
	INC	DE			;
	LD	A,D			;
	OR	E			;
	JP	Z,IDE_TO2		;
	IN	A,(IDESTTS)		; WAIT FOR DRIVE'S 512 BYTE READ BUFFER 
	AND	%00001000		; TO FILL (OR READY TO FILL)
	JP	Z,IDE_WDRQ		;
	SCF				; CARRY 1 = OK
	RET				;
IDE_TO2:				;
	XOR	A			; CARRY 0 = TIMED OUT
	RET				;

;___IDE_READ_BUFFER_______________________________________________________________________________________
;
;	READ IDE BUFFER
;________________________________________________________________________________________________________			
IDE_READ_BUFFER:
	PUSH	HL			;
	LD	HL,SECTOR_BUFFER	;
	LD	B,0			; 256 WORDS (512 BYTES PER SECTOR)
IDEBUFRD:				;
	IN	A,(IDELO)		; LOW BYTE OF WORD FIRST	
	LD	(HL),A			;
	IN	A,(IDEHI)		; THEN HIGH BYTE OF WORD
	INC	HL			;
	LD	(HL),A			;
	INC	HL			;
	DEC	B			;
	JP	NZ,IDEBUFRD		;
	POP	HL			;
	RET

;___IDE_WRITE_BUFFER_______________________________________________________________________________________
;
;	WRITE TO IDE BUFFER
;________________________________________________________________________________________________________			
IDE_WRITE_BUFFER:
	PUSH	HL			;
	LD	HL,SECTOR_BUFFER	;
	LD	B,0			; 256 WORDS (512 BYTES PER SECTOR)
IDEBUFWT:				;
	INC	HL			;
	LD	A,(HL)			;
	DEC	HL			;
	OUT	(IDEHI),A		; SET UP HIGH LATCHED BYTE BEFORE
	LD	A,(HL)			;
	OUT	(IDELO),A		; WRITING WORD WITH WRITE TO LOW BYTE
	INC	HL			;
	INC	HL			;
	DEC	B			;
	JP	NZ,IDEBUFWT		;
	POP	HL			;
	RET		
	
;___IDE_SETUP_LDA________________________________________________________________________________________
;
;	SETUP IDE DRIVE FOR LDA OPERATION
;________________________________________________________________________________________________________			
IDE_SETUP_LBA:
	LD	A,(LBA_TARGET_LO) 	; LOAD LBA REGISTER 0 WITH SECTOR ADDRESS TO READ
	LD	(IDE_LBA0),A		;
	LD	A,(LBA_TARGET_LO+1)	; LOAD LBA REGISTER 1 WITH SECTOR ADDRESS TO READ
	LD	(IDE_LBA1),A		;
	LD	A,(LBA_TARGET_HI) 	; LOAD LBA REGISTER 2 WITH SECTOR ADDRESS TO READ
	LD	(IDE_LBA2),A		;
	LD	A,(LBA_TARGET_HI+1)	; LOAD LBA REGISTER 3 WITH SECTOR ADDRESS TO READ
	AND	%00001111		; ONLY LOWER FOUR BITS ARE VALID
	ADD	A,%11100000		; ENABLE LBA BITS 5:7=111 IN IDE_LBA3
	LD	(IDE_LBA3),A		;
					; READ IDE HD SECTOR
	LD	A,1			;
	OUT	(IDESECTC),A		; SET SECTOR COUNT = 1	
					;	
	LD	A,(IDE_LBA0)		;
	OUT	(IDESECTN),A		; SET LBA 0:7
					;
	LD	A,(IDE_LBA1)		;
	OUT	(IDECYLLO),A		; SET LBA 8:15
					;
	LD	A,(IDE_LBA2)		;
	OUT	(IDECYLHI),A		; SET LBA 16:23
					;
	LD	A,(IDE_LBA3)		;
	AND	%00001111		; LOWEST 4 BITS USED ONLY
	OR	%11100000		; TO ENABLE LBA MODE
	OUT	(IDEHEAD),A		; SET LBA 24:27 + BITS 5:7=111
	CALL	IDESEGDISPLAY		;
	RET	

;___ATAPI_SOFT_RESET_____________________________________________________________________________________
;
;	RESET ATAPI BUS
;________________________________________________________________________________________________________			
ATAPI_SOFT_RESET:
	LD	A,%00001110		;NO INTERRUPTS, RESET DRIVE = 1
	OUT	(IDECTRL),A		;
	CALL	DELAY24			;
	LD	A,%00001010		;NO INTERRUPTS, RESET DRIVE = 0
	OUT	(IDECTRL),A		;
	CALL	ATAPI_WAIT_BUSY_READY	;
	RET	NC			; ERROR, RETURN
	CALL	ATAPI_DEVICE_SELECTION	;
	CALL	DELAY24			;
	CALL 	REQUEST_SENSE_LOOP	;
	RET

;___REQUEST_SENSE_LOOP____________________________________________________________________________________
;
;	ATAPI_REQUEST SENSE DATA
;_________________________________________________________________________________________________________			
REQUEST_SENSE_LOOP:
	LD	HL,ATAPI_REQUEST_SENSE	;
	CALL	ATAPI_SEND_PACKET	;
	CALL	ATAPI_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC			; ERROR, RETURN
	LD	B,0			; 256 WORDS (512 BYTES PER SECTOR)
REQUEST_SENSE_LOOP1:			;
	IN	A,(IDELO)		;
	INC	IX			;
	IN	A,(IDEHI)		;
	INC	IX			;
	DJNZ	REQUEST_SENSE_LOOP1	;
	RRD				; DELAY ONLY
	IN	A,(IDESTTS)		;READ ERROR REG
	AND	%00000001		;MASK OFF BIT
	JR	NZ,REQUEST_SENSE_LOOP	;
	RET

;___ATAPI_DEVICE_SELECTION________________________________________________________________________________
;
;	ATAPI DEVICE SELECTION
;_________________________________________________________________________________________________________			
ATAPI_DEVICE_SELECTION:

	LD	A,(IDEDEVICE)		; Selects Device
	OR	0A0H			;
	OUT	(IDEHEAD),A		;	
	RET				;



;__ATAPI_READ_SECTOR_____________________________________________________________________________________________________________ 
;  READ ATAPI SECTOR   
;
;   D E H L = SECTOR (DOUBLE WORD) TO READ 
;________________________________________________________________________________________________________________________________ 
ATAPI_READ_SECTOR:
	CALL	ISCUR			;
	JP	Z,ATAPI_READ_DATA_EXIT	;
	LD	A,(LBA_TARGET_LO)	; LOAD LBA REGISTER 0 WITH SECTOR ADDRESS TO READ
	LD	(READ_DISK_PACKET+5),A	;
	LD	A,(LBA_TARGET_LO+1)	; LOAD LBA REGISTER 1 WITH SECTOR ADDRESS TO READ
	LD	(READ_DISK_PACKET+4),A	;
	LD	A,(LBA_TARGET_HI)	; LOAD LBA REGISTER 2 WITH SECTOR ADDRESS TO READ
	LD	(READ_DISK_PACKET+3),A	;
	LD	A,(LBA_TARGET_HI+1)	; LOAD LBA REGISTER 3 WITH SECTOR ADDRESS TO READ
	LD	(READ_DISK_PACKET+2),A	;
	CALL	ATAPISEGDISPLAY		;
	CALL	REQUEST_SENSE_LOOP	; GET ATAPI SENSE CODES TO CLEAR ERRORS
	LD	HL,READ_DISK_PACKET	; SET POINTER TO READ SECTOR PACKET
	CALL	ATAPI_SEND_PACKET	; SEND PACKET COMMAND
	CALL	ATAPI_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC			; ERROR, RETURN
	LD	B,0			; 256 WORDS (512 BYTES PER SECTOR)
	LD	IX,SECTOR_BUFFER	;
	IN	A,(IDESTTS)		; READ  REG
	AND	%00001000		; MASK OFF BIT
	CP	08H			; IS DATA WAITING?
	JR	NZ,ATAPI_READ_DATA_EXIT	; NO, EXIT
ATAPI_READ_DATA_LOOP:
	IN	A,(IDELO)		;
	LD	(IX),A			;
	INC	IX			;
	IN	A,(IDEHI)		;
	LD	(IX),A			;
	INC	IX			;
	DJNZ	ATAPI_READ_DATA_LOOP	;
ATAPI_READ_DATA_EXIT:
	LD	HL,(pSECTOR)		; STORE PHYSICAL SECTOR IN BUFFER
	LD	(cuSECTOR),HL		; 
	LD	HL,(pTRACK)		; STORE PHYSICAL DISK TRACK IN BUFFER
	LD	(cuTRACK),HL		; 
	LD	A,(DISKNO)		; STORE CURRENT DRIVE IN BUFFER
	LD	(cuDISK),A		;
	SCF				; CARRY = 1 ON RETURN = OPERATION OK
	RET				;



;__ATAPI_WRITE_SECTOR_____________________________________________________________________________________________________________ 
;  WRITE ATAPI SECTOR   
;
;   D E H L = SECTOR (DOUBLE WORD) TO WRITE 
;________________________________________________________________________________________________________________________________ 
ATAPI_WRITE_SECTOR:

	LD	A,(LBA_TARGET_LO)	; LOAD LBA REGISTER 0 WITH SECTOR ADDRESS TO READ
	LD	(WRITE_DISK_PACKET+5),A	;
	LD	A,(LBA_TARGET_LO+1)	; LOAD LBA REGISTER 1 WITH SECTOR ADDRESS TO READ
	LD	(WRITE_DISK_PACKET+4),A	;
	LD	A,(LBA_TARGET_HI)	; LOAD LBA REGISTER 2 WITH SECTOR ADDRESS TO READ
	LD	(WRITE_DISK_PACKET+3),A	;
	LD	A,(LBA_TARGET_HI+1)	; LOAD LBA REGISTER 3 WITH SECTOR ADDRESS TO READ
	LD	(WRITE_DISK_PACKET+2),A	;
	CALL	ATAPISEGDISPLAY
	CALL 	REQUEST_SENSE_LOOP	;
	LD	HL,WRITE_DISK_PACKET	; SET POINTER TO WRITE PACKET COMMAND
	CALL	ATAPI_SEND_PACKET	; SEND THE PACKET COMMAND						
	CALL	ATAPI_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC			; ERROR, RETURN
	LD	B,0			; 256 WORDS (512 BYTES PER SECTOR)
	LD	IX,SECTOR_BUFFER	;
ATAPI_WRITE_DATA_LOOP:
	IN	A,(IDESTTS)		; READ  REG
	LD	A,(IX)			;
	PUSH    AF			;
	INC	IX			;
	LD	A,(IX)			;
	OUT	(IDEHI),A		;
	POP	AF			;
	OUT	(IDELO),A		;
	INC	IX			;
	DJNZ	ATAPI_WRITE_DATA_LOOP	;
	SCF				; CARRY = 1 ON RETURN = OPERATION OK
	RET				;




;__ATAPI_SEND_PACKET_____________________________________________________________________________________________________________ 
;  SEND PACKET POINTED TO BY HL TO ATAPI DRIVE   
;
;________________________________________________________________________________________________________________________________ 
ATAPI_SEND_PACKET:

	CALL	ATAPI_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC			; ERROR, RETURN
	CALL	IDE_WAIT_DRQ_ZERO	;
					;
	LD	A,0AH			;
	OUT	(IDECTRL),A		; Disable INT
	LD	A,00H			;
	OUT	(IDEERR),A		;
	LD	A,00H			;
	OUT	(IDESECTC),A		; 
	LD	A,00H			;
	OUT	(IDESECTN),A		;
	LD	A,00H			;
	OUT	(IDECYLLO),A		; 
	LD	A,60H			;
	OUT	(IDECYLHI),A		; 
	LD	A,(IDEDEVICE)		;
	OUT	(IDEHEAD),A		; Bit 4 selects device
	LD	A,0A0H			;
	OUT	(IDESTTS),A		;
					;
	CALL	IDE_WAIT_DRQ_READY	; MAKE SURE DRIVE IS READY TO PROCEED
					;
	LD	B,6			; SEND 12 BYTES (6 WORDS)
					;
ATAPI_SEND_PACKET_LOOP:
	LD	A,(HL)			; GET BYTE
	LD	D,A			; STORE LOW BYTE IN D
	INC	HL			; INC POINTER
	LD	A,(HL)			; GET HIGH BYTE
	OUT	(IDEHI),A		; STORE HIGH BYTE
	LD	A,D			; MOVE LOW BYTE INTO A
	OUT	(IDELO),A		; STORE LOW BYTE
	INC	HL			; INC POINTER
	IN	A,(IDECTRL)		; GET STATUS
	DJNZ	ATAPI_SEND_PACKET_LOOP	; LOOP
					;
	CALL	ATAPI_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC			; ERROR, RETURN
	IN	A,(IDECTRL)		; READ STATUS (FOR DELAY)
					;
	RET				;
	
;__SETUPDRIVE__________________________________________________________________________________________________________________________ 
;
;	SETUP FLOPPY DRIVE SETTINGS 
;________________________________________________________________________________________________________________________________
;
;
;
SETUPDRIVE:
	LD	A,RESETL		; RESET SETTINGS
	OR	MINI			; SELECT MINI FLOPPY (low dens=1, high dens=0)
	OR	PRECOMP			; SELECT PRECOMP 
	OR	FDDENSITY		; SELECT DENSITY
	OR	FDREADY			; SELECT READY SIGNAL
	LD	(FLATCH_STORE),A	; SAVE SETTINGS
	LD	A,01H			;
	LD	(UNIT),A		; SET UNIT 1
	LD	A,2			; DENSITY
	LD	(DENS),A		;
	LD	A,09			;
	LD	(EOTSEC),A		; LAST SECTOR OF TRACK			
	LD	A,7FH			;
	LD	(SRTHUT),A		; STEP RATE AND HEAD UNLOAD TIME
	LD	A,05H			;
	LD	(HLT),A			; HEAD LOAD TIME
	LD	A,0DH			;
	LD	(GAP),A			; GAP 
	LD	A,80H			;
	LD	(SECSIZ),A		; SECTOR SIZE /4
					;
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
					;					
	LD	HL,FLATCH_STORE		; POINT TO FLATCH
	RES	1,(HL)			; SET MOTOR ON
	CALL	OUTFLATCH		; OUTPUT TO CONTROLLER
	NOP				;
	NOP				;
	LD	A,00H			; ZERO TRACK
	LD	(pTRACK),A		; STORE TRACK
	CALL	SETTRACK		; DO IT	
	NOP				;
	NOP				;
	LD	HL,FLATCH_STORE		; POINT TO FLATCH
	SET	1,(HL)			; SET MOTOR OFF
	CALL	OUTFLATCH		; OUTPUT TO CONTROLLER
	RET
;
;__OUTFLATCH__________________________________________________________________________________________________________________________ 
;
;	SEND SETTINGS TO FLOPPY CONTROLLER
;________________________________________________________________________________________________________________________________
;
OUTFLATCH:
	LD	A,(FLATCH_STORE)	; SET A TO SETTINGS
	OUT	(FLATCH),A		; OUTPUT TO CONTROLLER
	RET

		
;__FLOPPYREAD__________________________________________________________________________________________________________________________ 
;
; 	READ A FLOPPY DISK SECTOR 	
;________________________________________________________________________________________________________________________________
;	
FLOPPYREAD:
	CALL	SEGDISPLAY		;
	LD	A,46H			; BIT 6 SETS MFM, 06H IS READ COMMAND
	LD	(CMD),A			;
	JP	DSKOP			;
;
;__FLOPPYWRITE__________________________________________________________________________________________________________________________ 
;
; 	WRITE A FLOPPY DISK SECTOR 	
;________________________________________________________________________________________________________________________________
;	
FLOPPYWRITE:
	CALL	SEGDISPLAY		;
	LD	A,45H			; BIT 6 SETS MFM, 05H IS WRITE COMMAND
	LD	(CMD),A			;
	JP	DSKOP			;
;
;__DSKOP__________________________________________________________________________________________________________________________ 
;
; 	PERFORM A DISK OPERATION 	
;________________________________________________________________________________________________________________________________
;		
DSKOP:
	LD	HL,FLATCH_STORE		; POINT TO FLATCH
	SET	1,(HL)			; SET MOTOR OFF
	CALL	OUTFLATCH		; OUTPUT TO CONTROLLER
					;
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CP	0FFH			; DID IT RETURN WITH ERROR CODE?
	JP	Z,DSKEXIT		; IF YES, EXIT WITH ERROR CODE
					;	
	LD	A,(UNIT)		; GET DISK UNIT NUMBER
	AND	03H			; MASK FOR FOUR DRIVES.
	LD	B,A			; PARK IT IN B
	LD	A,(HEAD)		; GET HEAD SELECTION
	AND	01H			; INSURE SINGLE BIT
	RLA				;
	RLA				; MOVE HEAD TO BIT 2 POSITION
	OR	B			; OR HEAD TO UNIT BYTE IN COMMAND BLOCK
	LD	(UNIT),A		; STORE IN UNIT
					;
	LD	HL,FLATCH_STORE		; POINT TO FLATCH
	RES	1,(HL)			; SET MOTOR ON
	CALL	OUTFLATCH		; OUTPUT TO CONTROLLER	
					;
	LD	A,03H			; SPECIFY COMMAND
	CALL	PFDATA			; PUSH IT
	LD	A,(SRTHUT)		; STEP RATE AND HEAD UNLOAD TIME
	CALL	PFDATA			; PUSH THAT
	LD	A,(HLT)			;
	CALL	PFDATA			; PUSH THAT
					;
	CALL	SETTRACK		; PERFORM SEEK TO TRACK
					;
	JP	NZ,DSKEXIT		; IF ERROR, EXIT
					;
	LD	A,(CMD)			; WHAT COMMAND IS PENDING?
	OR	A			; SET FLAGS
	JP	DOSO4			; NO, MUST BE READ OR WRITE COMMAND
DSKEXIT:	
	LD	HL,FLATCH_STORE		; POINT TO FLATCH
	SET	1,(HL)			; SET MOTOR OFF
	CALL	OUTFLATCH		; OUTPUT TO CONTROLLER
					;
	OR	0FFH			; SET -1 IF ERROR
	RET

RESULT:
	LD	C,07H			; LOAD C WITH NUMBER OF STATUS BYTES
	LD	HL,ST0			; POINT TO STATS STORAGE
RS3:
	CALL	GFDATA			; GET FIRST BYTE
	LD	(HL),A			; SAVE IT
	INC	HL			; POINTER++
	DEC	C			; CC-1
	JP	NZ,RS3			; LOOP TIL C0
	LD	A,(ST0)			; LOAD STS0
	AND	0F8H			; MASK OFF DRIVE #
	LD	B,A			; PARK IT
	LD	A,(ST1)			; LOAD STS1
	OR	B			; ACC OR B ->ACC IF 0 THEN SUCCESS
					;
RSTEXIT:
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	LD	HL,FLATCH_STORE		; POINT TO FLATCH
	SET	1,(HL)			; SET MOTOR OFF
	CALL	OUTFLATCH		; OUTPUT TO CONTROLLER
					;
	CALL	SEGDISPLAY		;
	RET				; DONE RETURN TO CALLER.
	
	
DOSO4:
					;
	LD	HL,SECTOR_BUFFER	; GET BUFFER ADDRESS TO HL
	LD	A,(SECSIZ)		; XFERLEN
	LD	C,A			; C WILL BE THE NUMBER OF TRANSACTIONS
					; DIVIDED BY 4
					;
	LD	A,(CMD)			;
	CALL	PFDATA			; PUSH COMMAND TO I8272
	LD	A,(UNIT)		;
	CALL	PFDATA			; 
	LD	A,(pTRACK)		;
	CALL	PFDATA			; 
	LD	A,(HEAD)		;
	CALL	PFDATA			; 
	LD	A,(pSECTOR)		;
	INC	A			;
	CALL	PFDATA			; 
	LD	A,(DENS)		;
	CALL	PFDATA			; WHAT DENSITY
	LD	A,(EOTSEC)		;
	CALL	PFDATA			; ASSUME SC (SECTOR COUNT)  EOT
	LD	A,(GAP)			;
	CALL	PFDATA			; WHAT GAP IS NEEDED
	LD	A,(DTL)			; DTL, IS THE LAST COMMAND BYTE TO I8272
	CALL	PFDATAS
	LD	A,(CMD)			; READ IS 0 IS THIS A READ OR WRITE?
	AND	%00000001		; WRITE IS 1
	JP	NZ,WRR			; JMP WRITE IF 1
;

	;
; PERFORM READ
; LOOP EXECUTES 4X, THIS ALLOWS C RATHER THAN BC AS COUNTER
; SAVING A FEW TSTATES. MAKES UP TO 1024 BYTE SECTORS POSSIBLE.
; FROM READ TO READ MUST NOT EXCEED 25US WORST CASE MIN.
; (76T STATES FOR 3MHZ 8085) or (100 T STATES FOR 4MHZ Z80)
;

RDD_POLL:
FDC_READP0:
	IN	A,(FMSR)		;
	OR	A			; test if byte ready RQM1
	JP	P,FDC_READP0		;	
					;
	AND	20H			;
	JP	Z,DSKOPEND		;jump if in results mode
					;
	IN	A,(FDATA)		;
	LD	(HL),A			;
	INC	HL			;

FDC_READP1:
	IN	A,(FMSR)		;
	OR	A			;
	JP	P,FDC_READP1		;
					;
	AND	20H			;
	JP	Z,DSKOPEND		;
					;
	IN	A,(FDATA)		;
	LD	(HL),A			;
	INC	HL			;
					;
FDC_READP2:
	IN	A,(FMSR)		;
	OR	A			;
	JP	P,FDC_READP2		;
					;
	AND	20H			;
	JP	Z,DSKOPEND		;
					;
	IN	A,(FDATA)		;
	LD	(HL),A			;
	INC	HL			;
					;
FDC_READP3:
	IN	A,(FMSR)		; 11
	OR	A			; 4
	JP	P,FDC_READP3		; 10
					;
	AND	20H			; 7
	JP	Z,DSKOPEND		; 10
					;
	IN	A,(FDATA)		; 11
	LD	(HL),A			; 10
	INC	HL			; 11
					;
	DEC	C			; 4
	JP	NZ,FDC_READP0		; 11

DSKOPEND:
	LD	HL,FLATCH_STORE		; POINT TO FLATCH
	SET	0,(HL)			; SET TC
	CALL	OUTFLATCH		; OUTPUT TO CONTROLLER
	NOP				;
	NOP				; 2 MICROSECOND DELAY
	RES	0,(HL)			; RESET TC
	CALL	OUTFLATCH		; OUTPUT TO CONTROLLER
	NOP				;
	NOP				; 2 MICROSECOND DELAY
	NOP				;
	NOP				; 2 MICROSECOND DELAY
	SET	1,(HL)			; TURN OFF MOTOR
	CALL	OUTFLATCH		; OUTPUT TO CONTROLLER
	JP	RESULT			; GET STATUS BYTES <RESULT PHASE>
WRR:
FDC_WRITEP0:
	IN	A,(FMSR)		;
	OR	A			; test if byte ready RQM1
	JP	P,FDC_WRITEP0		;	
					;
	AND	20H			;
	JP	Z,DSKOPEND		;jump if in results mode
					;
	LD	A,(HL)			;
	OUT	(FDATA),A		;	
	INC	HL			;

FDC_WRITEP1:
	IN	A,(FMSR)		;
	OR	A			;
	JP	P,FDC_WRITEP1		;
					;
	AND	20H			;
	JP	Z,DSKOPEND		;
					;
	LD	A,(HL)			;
	OUT	(FDATA),A		;	
	INC	HL			;
					;
FDC_WRITEP2:
	IN	A,(FMSR)		;
	OR	A			;
	JP	P,FDC_WRITEP2		;
					;
	AND	20H			;
	JP	Z,DSKOPEND		;
					;
	LD	A,(HL)			;
	OUT	(FDATA),A		;	
	INC	HL			;
					;
FDC_WRITEP3:
	IN	A,(FMSR)		; 11
	OR	A			; 4
	JP	P,FDC_WRITEP3		; 10
					;
	AND	20H			; 7
	JP	Z,DSKOPEND		; 10
					;
	LD	A,(HL)			;
	OUT	(FDATA),A		;	
	INC	HL			; 11
					;
	DEC	C			; 4
	JP	NZ,FDC_WRITEP0		; 11
	JP	DSKOPEND		; 10
	
		
;__SETTRACK__________________________________________________________________________________________________________________________ 
;
; 	SEEK TO A TRACK ON GIVEN UNIT
; 	A: TRACK #
;________________________________________________________________________________________________________________________________
;
SETTRACK:
	LD	A,(fTRACK)		; GET CURRENT HEAD TRACK
	LD	C,A
	LD	A,(pTRACK)		; GET TRACK
	OR	A			; SET FLAGS
	JP	Z,RECAL			; IF 0 PERFORM RECAL INSTEAD OF SEEK
	CP	C			;
	JP	Z,WAINT			; ALREADY THERE, ABORT
	LD	(fTRACK),A		; STORE TRACK
	LD	A,0FH			; SEEK COMMAND
	CALL	PFDATA			; PUSH COMMAND
	LD	A,(UNIT)		; SAY WHICH UNIT
	CALL	PFDATA			; SEND THAT
	LD	A,(pTRACK)		; TO WHAT TRACK
	CALL	PFDATA			; SEND THAT TOO
	JP	WAINT			; WAIT FOR INTERRUPT SAYING DONE
RECAL:
	LD	A,00H			;
	LD	(fTRACK),A		; STORE TRACK
	LD	A,07H			; RECAL TO TRACK 0
	CALL	PFDATA			; SEND IT
	LD	A,(UNIT)		; WHICH UNIT
	CALL	PFDATA			; SEND THAT TOO
;
WAINT:
;
	CALL	DELAYHSEC		; DELAY TO LET HEADS SETTLE BEFORE READ
					;
					; WAIT HERE FOR INTERRPT SAYING DONE
					; LOOP TIL INTERRUPT
	CALL	CHECKINT		; CHECK INTERRUPT STATUS
;
	RET
	
	
;__CYCLEFLOPPY__________________________________________________________________________________________________________________________ 
;
; 	SEEK TO TRACK 0, THEN BACK TO THE SELECTED TRACK.   
;	THIS CAN BE USED ON AN ERROR CONDITION TO VERIFY THAT HEAD IS ON SELECTED TRACK
; 	
;________________________________________________________________________________________________________________________________
;
CYCLEFLOPPY:
	PUSH	AF			; STORE	AF
	PUSH	HL			; STORE	HL
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	CALL	CHECKINT		; CHECK INTERRUPT STATUS, MAKE SURE IT IS CLEAR
	LD	HL,FLATCH_STORE		; POINT TO FLATCH
	RES	1,(HL)			; SET MOTOR ON
	CALL	OUTFLATCH		; OUTPUT TO CONTROLLER
	NOP				;
	NOP				;
	CALL	RECAL			;
	CALL	DELAYHSEC		;
	CALL	DELAYHSEC		;
	CALL	DELAYHSEC		;
	CALL	DELAYHSEC		;
	CALL	RECAL			;
	CALL	DELAYHSEC		;
	CALL	DELAYHSEC		;
	CALL	DELAYHSEC		;
	CALL	DELAYHSEC		;
	CALL	SETTRACK		;
	CALL	DELAYHSEC		;
	CALL	DELAYHSEC		;
	CALL	DELAYHSEC		;
	CALL	DELAYHSEC		;
	POP	HL			;
	POP	AF			; RESTORE AF
	RET	

;__PFDATAS__________________________________________________________________________________________________________________________ 
;
; WRITE A COMMAND OR PARAMETER S.EQUENCE
;
; TRANSFERS ARE SYNCHONIZED BYT MSR D7 <RQM> AND D6 <DIO>
;	RQM  DIO
;	0	0	BUSY
;	1	0	WRITE TO DATA REGISTER PERMITTED
;	1	1	BYTE FOR READ BY HOST PENDING
;	0	1	BUSY
;
;________________________________________________________________________________________________________________________________
;
PFDATAS:
	PUSH	AF			; STORE AF
PFDS1:
	IN	A,(FMSR)		; READING OR WRITING IS KEYS TO D7 RQM
	AND	80H			; MASK OFF RQM BIT 
	JP	Z,PFDS1			; WAIT FOR RQM TO BE TRUE.
	IN	A,(FMSR)		; READ STATUS
	AND	40H			; WAITING FOR INPUT?
	CALL	NZ,ERRORT		; NO, SIGNAL ERROR
	POP	AF			; RESTORE AF
	OUT	(FDATA),A		; OUTPUT A TO CONTROLLER
	RET		
	
;__PFDATA__________________________________________________________________________________________________________________________ 
;
; WRITE A COMMAND OR PARAMETER S.EQUENCE
;
; TRANSFERS ARE SYNCHONIZED BYT MSR D7 <RQM> AND D6 <DIO>
;	RQM  DIO
;	0	0	BUSY
;	1	0	WRITE TO DATA REGISTER PERMITTED
;	1	1	BYTE FOR READ BY HOST PENDING
;	0	1	BUSY
;
;________________________________________________________________________________________________________________________________
;
PFDATA:
	PUSH	AF			; STORE AF
PFD1:
	IN	A,(FMSR)		; READING OR WRITING IS KEYS TO D7 RQM
	AND	80H			; MASK OFF RQM BIT 
	JP	Z,PFD1			; WAIT FOR RQM TO BE TRUE.
	IN	A,(FMSR)		; READ STATUS
	AND	40H			; WAITING FOR INPUT?
	CALL	NZ,ERRORT		; NO, SIGNAL ERROR
	POP	AF			; RESTORE AF
	OUT	(FDATA),A		; OUTPUT A TO CONTROLLER
	JP	DELAY24			; DELAY 24US
	
	
	
;__DELAY24__________________________________________________________________________________________________________________________ 
;
; 	DELAY 24US
;________________________________________________________________________________________________________________________________
;
	
DELAY24:	
					; JP= 10T	
	PUSH	IX			; 15T
	POP	IX			; 14T
	PUSH	IX			; 15T
	POP	IX			; 14T
DELAY12:
	PUSH	IX			; 15T
	POP	IX			; 14T
	RET				; 10T


;__CHECKINT__________________________________________________________________________________________________________________________ 
;
; CHECK FOR ACTIVE FDC INTERRUPTS BEFORE GIVING I8272 COMMANDS
; POLL RQM FOR WHEN NOT BUSY AND THEN SEND FDC
; SENSE INTERRUPT COMMAND.  IF IT RETURNS WITH NON ZERO
; ERROR CODE, PASS BACK TO CALLING ROUTINE FOR HANDLING
;________________________________________________________________________________________________________________________________
;
CHECKINT:
	IN	A,(FMSR)		; READING OR WRITING IS KEYS TO D7 RQM
	AND	80H			; MASK OFF RQM BIT
	JP	Z,CHECKINT		; WAIT FOR RQM TO BE TRUE. WAIT UNTIL DONE
	IN	A,(FMSR)		; READ STATUS
	AND	40H			; WAITING FOR INPUT?
	JP	NZ,CHECKINTDONE		; NO, SIGNAL ERROR
	CALL	SENDINT			; SENSE INTERRUPT COMMAND
CHECKINTDONE:
	RET				;
	

;__DELAYHSEC__________________________________________________________________________________________________________________________ 
;
; DELAY FOR 1/2 SECOND
;________________________________________________________________________________________________________________________________
;		
DELAYHSEC:
	LD	HL,00000H		; 65536
DELDM:
	NOP				; (4 T) 
	NOP				; (4 T)
	NOP				; (4 T)
	NOP				; (4 T)
	DEC	L			; (6 T)
	JP	NZ,DELDM		; (10 T) 24 T  8 MICROSECONDS AT 4 MHZ
	DEC	H			; (6 T)
	JP	NZ,DELDM		; (10 T) (8 US * 256) * 256  524288 US  .5 SECONDS
	RET

;__ERRORT__________________________________________________________________________________________________________________________ 
;
; ERROR HANDLING
;________________________________________________________________________________________________________________________________
;			
ERRORT:
	IN	A,(FDATA)		; CLEAR THE JUNK OUT OF DATA REGISTER
	IN	A,(FMSR)		; CHECK WITH RQM
	AND	80H			; IF STILL NOT READY, READ OUT MORE JUNK
	JP	Z,ERRORT		;
	LD	A,0FFH			; RETURN ERROR CODE -1
					;
	RET
;__SENDINT__________________________________________________________________________________________________________________________ 
;
; SENSE INTERRUPT COMMAND
;________________________________________________________________________________________________________________________________
;			
SENDINT:
	LD	A,08H			; SENSE INTERRUPT COMMAND
	CALL	PFDATA			; SEND IT
	CALL	GFDATA			; GET RESULTS
	LD	(ST0A),A		; STORE THAT
	AND	0C0H			; MASK OFF INTERRUPT STATUS BITS
	CP	80H			; CHECK IF INVALID COMMAND
	JP	Z,ENDSENDINT		; YES, EXIT
	CALL	GFDATA			; GET ANOTHER (STATUS CODE 1)
	LD	(ST1A),A		; SAVE THAT
	LD	A,(ST0A)		; GET FIRST ONE
	AND	0C0H			; MASK OFF ALL BUT INTERRUPT CODE 00 IS NORMAL
ENDSENDINT:
	RET				;ANYTHING ELSE IS AN ERROR


;__GFDATA__________________________________________________________________________________________________________________________ 
;
; GET DATA FROM FLOPPY CONTROLLER
;
; TRANSFERS ARE SYNCHONIZED BYT MSR D7 <RQM> AND D6 <DIO>
;	RQM  DIO
;	0	0	BUSY
;	1	0	WRITE TO DATA REGISTER PERMITTED
;	1	1	BYTE FOR READ BY HOST PENDING
;	0	1	BUSY
;
;________________________________________________________________________________________________________________________________
;		
GFDATA:
	IN	A,(FMSR)		; READ STATUS BYTE
	AND	80H			; MASK OFF RQM
	JP	Z,GFDATA		; LOOP WHILE BUSY
	IN	A,(FMSR)		; READ STSTUS BUTE
	AND	40H			; MASK OFF DIO
	CALL	Z,ERRORT		; IF WRITE EXPECTED RUN ERRORRT
	IN	A,(FDATA)		; READ DATA
	JP	DELAY24			; DELAY 24US
	

; PIO 82C55 I/O IS DECODED TO PORT 60-67
;
PORTA		.EQU 	60H
PORTB		.EQU 	61H
PORTC		.EQU 	62H
PIOCONT 	.EQU 	63H	

;__IDESEGDISPLAY________________________________________________________________________________________
;
;  Display contents of IDE LOGICAL BLOCK ADDRESS ON DSKY    
;____________________________________________________________________________________________________
IDESEGDISPLAY:
	LD	A, 82H			;
	OUT (PIOCONT),A			;
					;
	LD	A,(IDE_LBA3)		;
	AND	0FH			;
	LD	(DISPLAYBUF+6),A	;
	LD	A,(IDE_LBA3)		;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+7),A	;
					;
	LD	A,(IDE_LBA2)		;
	AND	0FH			;
	LD	(DISPLAYBUF+4),A	;
	LD	A,(IDE_LBA2)		;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+5),A	;
					;
	LD	A,(IDE_LBA1)		;
	AND	0FH			;
	LD	(DISPLAYBUF+2),A	;
	LD	A,(IDE_LBA1)		;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+3),A	;
	
	LD	A,(IDE_LBA0)		;
	AND	0FH			;
	LD	(DISPLAYBUF),A		;
	LD	A,(IDE_LBA0)		;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+1),A	;
	JP	SEGDISPLAY1		;
;__ATAPISEGDISPLAY________________________________________________________________________________________
;
;  Display contents of ATAPI LOGICAL BLOCK ADDRESS ON DSKY    
;____________________________________________________________________________________________________
ATAPISEGDISPLAY:
	LD	A, 82H			;
	OUT (PIOCONT),A			;
					;
	LD	A,(LBA_TARGET_HI+1)	;
	AND	0FH			;
	LD	(DISPLAYBUF+6),A	;
	LD	A,(LBA_TARGET_HI+1)	;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+7),A	;
					;
	LD	A,(LBA_TARGET_HI)	;
	AND	0FH			;
	LD	(DISPLAYBUF+4),A	;
	LD	A,(LBA_TARGET_HI)	;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+5),A	;
					;
	LD	A,(LBA_TARGET_LO+1)	;
	AND	0FH			;
	LD	(DISPLAYBUF+2),A	;
	LD	A,(LBA_TARGET_LO+1)	;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+3),A	;
	
	LD	A,(LBA_TARGET_LO)	;
	AND	0FH			;
	LD	(DISPLAYBUF),A		;
	LD	A,(LBA_TARGET_LO)	;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+1),A	;
	JP	SEGDISPLAY1		;

;__SEGDISPLAY________________________________________________________________________________________
;
;  Display contents of TRACK, SECTOR, ST0, ST1 ON DSKY
;     
;____________________________________________________________________________________________________
SEGDISPLAY:
	LD	A, 82H			;
	OUT (PIOCONT),A			;
	LD	A,(TRACK)		;
	AND	0FH			;
	LD	(DISPLAYBUF+6),A	;
	LD	A,(TRACK)		;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+7),A	;			
	LD	A,(SECTOR)		;
	AND	0FH			;
	LD	(DISPLAYBUF+4),A	;
	LD	A,(SECTOR)		;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+5),A	;
	LD	A,(ST0)			;
	AND	0FH			;
	LD	(DISPLAYBUF+2),A	;
	LD	A,(ST0)			;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+3),A	;			
	LD	A,(ST1)			;
	AND	0FH			;
	LD	(DISPLAYBUF),A		;
	LD	A,(ST1)			;
	AND	0F0H			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	SRL	A			;
	LD	(DISPLAYBUF+1),A	;
SEGDISPLAY1:				;
	LD	HL,DISPLAYBUF		;
	LD	BC,0007H		;
	ADD	HL,BC			;
	LD	B,08H			; SET DIGIT COUNT
	LD	A,40H			; set Control port 7218 to off
	OUT	(PORTC),A		; output
	CALL 	DELAY12			; wait
	LD	A,0D0H			; set control to 1111 (Data Coming, Hex Decode, Decode, Normal)
	OUT	(PORTA),A		; output to port
	LD	A,80H			; Strobe write pulse with Control=1
	OUT	(PORTC),A		; output to port
	CALL 	DELAY12			; wait
	LD	A,40H			; set Control port 7218 to off
	OUT	(PORTC),A		; output
	CALL 	DELAY12			; wait
SEGDISPLAY_LP:		
	LD	A,(HL)			; GET DISPLAY DIGIT
	OUT	(PORTA),A		; OUT TO PORTA
	LD	A,00H			; SET WRITE STROBE
	OUT	(PORTC),A		; OUT TO PORTC
	CALL	DELAY12			; DELAY
	LD	A,40H			; SET CONTROL PORT OFF
	OUT	(PORTC),A		; OUT TO PORTC
	CALL	DELAY12			; WAIT
	DEC	HL			; INC POINTER
	DJNZ	SEGDISPLAY_LP		; LOOP FOR NEXT DIGIT
	RET	
	
DISPLAYBUF:	.DB 	01,02,03,04,05,06,07,08
		.DB	00,00,00,00,00,00,00,00	
		.DB	00,00,00,00,00,00,00,00	
		.DB	00,00,00,00,00,00,00,00	
		.DB	00,00,00,00,00,00,00,00	
FLOPPYSTACK:	.DB	00
PARKSTACK:	.DB	00,00,00,00
		
READ_DISK_PACKET
		.DB	0A8H,00,00,00,00,01H,00,00,00,01H,00,00
WRITE_DISK_PACKET
		.DB	2AH,00,00,00,00,11H,00,00,01H,00,00,00
ATAPI_REQUEST_SENSE
		.DB	03H,00,00,00,011H,00,00,00,00,00,00,00
;
;	THE REMAINDER OF THE CBIOS IS RESERVED UNINITIALIZED
;	DATA AREA, AND DOES NOT NEED TO BE A PART OF THE
;	SYSTEM MEMORY IMAGE (THE SPACE MUST BE AVAILABLE,
;	HOWEVER, BETWEEN "BEGDAT" AND "ENDDAT").
;

;
; DISK COMMAND BLOCK
;
CMD:		.DB	0			; COMMAND READ OR WRITE,
UNIT:		.DB	0			; PHYSICAL DRIVE 0->3
HEAD:		.DB	0			; HEAD SEL 0 OR 1
DENS:		.DB	2			; DENSITY
EOTSEC:		.DB	09			; LAST SECTOR OF TRACK
GAP:		.DB	1BH			; VALUE FOR IRG <GAP3>
SECSIZ:		.DB	080H			; HOW MANY BYTES TO TRANSFER/4
DTL:		.DB	0FFH			; SIZE OF SECTOR
SRTHUT:		.DB	7FH			; STEP RATE AND HEAD UNLOAD TIME
HLT:		.DB	05H			; HEAD LOAD TIME
MIN:		.DB	MINI			; LATCH BIT PATTERN FOR FDC9229 MINITRUE
PRE:		.DB	PRECOMP			; LATCH BIT PATTERN FOR FDC9229 PRECOMP125NS
;
; FLOPPY STATUS RESULT STORAGE
;
ST0:		.DB	0			; STORE STATUS 0
ST1:		.DB	0			; ST1
ST2:		.DB	0			; ST2
SCYL:		.DB	0			; TRACK
SHEAD:		.DB	0			; HEAD 0 OR 1
SREC:		.DB	0			; SECTOR
SNBIT:		.DB	0			; DENSITY
ST0A:		.DB	0			; STORE STATUS 0
ST1A:		.DB	0			; ST1
RETRY		.DB	0			; RETRIES
RETRY1		.DB	0			; RETRIES

FLATCH_STORE:
		.DB	00


TRACK:		.DW 0			; TWO BYTES FOR TRACK # (LOGICAL)
pTRACK:		.DW 0			; TWO BYTES FOR TRACK # (PHYSICAL)
fTRACK:		.DW 0			; TWO BYTES FOR TRACK # (HEAD LOCATION)

PAGER:		.DB 1			; COPY OF PAGER BYTE
DB_PAGER:
		.DB 0FFH			; COPY OF PAGER BYTE (debug)
SECTOR:		.DW 0			; TWO BYTES FOR SECTOR # (LOGICAL)
pSECTOR: 	.DW 0			; TWO BYTES FOR SECTOR # (PHYSICAL)
V_SECTOR: 	.EQU pSECTOR		; TWO BYTES FOR VIRTUAL SECTOR #
SECST:		.DW 0			; SECTOR IN ROM/RAM START ADDRESS
DMAAD:		.DW 0			; DIRECT MEMORY ADDRESS
DISKNO:		.DB 0			; DISK NUMBER 0-15
LBA_OFFSET_LO:
		.DW 0			; IDE HD PARTITION STARTING SECTOR (LOW 16 BITS)
LBA_OFFSET_HI:
		.DW 0			; IDE HD PARTITION STARTING SECTOR (HI 16 BITS, 12 USED)
LBA_TARGET_LO:
		.DW 0			; IDE HD PARTITION TARGET SECTOR (LOW 16 BITS)
LBA_TARGET_HI:
		.DW 0			; IDE HD PARTITION TARGET SECTOR (HI 16 BITS, 12 USED)
IDEDEVICE:
		.DB 0			; ATAPI DEVICE SELECTION FLAG

IDE_LBA0:
		.DB 0			; SET LBA 0:7
IDE_LBA1:
		.DB 0			; SET LBA 8:15
IDE_LBA2:
		.DB 0			; SET LBA 16:23
IDE_LBA3:
		.DB 0			; LOWEST 4 BITS USED ONLY TO ENABLE LBA MODE 
SECTOR_INDEX:
		.DB 1			; WHERE 128 BYTE CP/M SECTOR IS IN 512 BYTE IDE HD SECTOR
;
;	SCRATCH RAM AREA FOR BDOS USE
BEGDAT:		.EQU $			; BEGINNING OF DATA AREA
DIRBF:		.DS 128			; SCRATCH DIRECTORY AREA
ALL00:		.DS 65			; ALLOCATION VECTOR 0  (DSM/8 = 1 BIT PER BLOCK)  44
ALL01:		.DS 33			; ALLOCATION VECTOR 1 (225/8)
ALL02:		.DS 256			; ALLOCATION VECTOR 2 (511/8)
ALL03:		.DS 256			; ALLOCATION VECTOR 3 (511/8)
ALL04:		.DS 65			; ALLOCATION VECTOR 4 (497/8)
ALL05:		.DS 65			; ALLOCATION VECTOR 4 (495/8)
ALL06:		.DS 65			; ALLOCATION VECTOR 4 (495/8)
CHK00:		.DS 5			; NOT USED FOR FIXED MEDIA
CHK01:		.DS 0			; NOT USED FOR FIXED MEDIA
CHK02:		.DS 0			; NOT USED FOR FIXED MEDIA
CHK03:		.DS 128			; NOT USED FOR FIXED MEDIA
CHK04:		.DS 0			; NOT USED FOR FIXED MEDIA
CHK05:		.DS 0			; NOT USED FOR FIXED MEDIA
CHK06:		.DS 0			; NOT USED FOR FIXED MEDIA
;
cuDISK:		.DS 1			; CURRENT PHYSICAL DISK ID IN BUFFER
cuSECTOR:			; CURRENT PHYSICAL DISK SECTOR IN BUFFER
		.DW 1
cuTRACK:			; CURRENT PHYSICAL DISK TRACK IN BUFFER
		.DW 2
SECTOR_BUFFER:
		.DS 512			; STORAGE FOR 512 BYTE IDE HD SECTOR
TMPBUF:	.EQU SECTOR_BUFFER
ENDDAT:	.EQU $			; END OF DATA AREA
DATSIZ:	.EQU $-BEGDAT		; SIZE OF DATA AREA






	.IF	CONDUSRPATCH
		; original is not to have this 
	.ELSE

;*****************************************
; Patch to print USER number in CCP prompt
;
USRPAT:

UPAT1:	CALL	GETUSR			;call ccp routine to
					; get current user number.
	CP	10			;check if zero thru nine
					; or if two digits
					; ten thru fifteen.
	JP	NC,UPDBL		;jump if two digits.
	ADD	A,30h			;else convert to ascii.
UPSING:	CALL	PRINT			;call ccp conout routine.
	RET				;return where ccp left off.
					;
UPDBL:	ADD	A,26h			;subtract 10 from the user
					; number; the remainder 
					; will be the second digit.
					; Add 30 to convert it to 
					; ascii or just add 26h to
					; accomplish both.
	PUSH	AF			;save the second digit
	LD	A,31h			;load an ascii 'one'
	CALL	PRINT			;call ccp conout routine
	POP	AF			;return second digit
	JP	UPSING			;output second digit
					; and return
	RET

	.ENDIF

;	******* TEXT STRINGS *******

TXT_RO_ERROR:
	.DB CR,LF
	.DB "ERROR: WRITE TO READ ONLY DISK"
	.DB END


TXT_STARTUP_MSG:

	.IF 	CONDSHORTMSG

		.DB CR,LF
		.DB "CP/M-80 VERSION 2.2C FOR THE "
		.DB "N8VEM - W/ATAPI & FLOPPY"
		.DB CR,LF
		.DB END

	.ELSE

		.DB "CP/M V2.2C"
		.DB END

	.ENDIF


	.IF 	CONDTSR
		; add nothing
	.ELSE

		; TSR entry points here - put a call xx then ret at 5 locations
		; can add any code above $F800 as this is not used by anything
		; extra code added by seperate programs usually run at startup
		; and these programs poke a jump at each of these locations
		; if not run, these routines simply return

		.ORG 0F2F0H		; add LCD output routine here F900-FBFF
TSR1:		ret
		.ORG 0F2F3H		; add counter here uses 64 bytes F800 to F83F
TSR2:		ret
		.ORG 0F2F6H		; add scan IBM keyboard routine here FC00 to FFFF
TSR3:		ret
		.ORG 0F2F9H		; echooff 16 bytes F840 to F84F
TSR4:		ret			
		.ORG 0F2FCH		; blank
TSR5:		ret



	.ENDIF


	.ORG 0FDFFH
LASTBYTE:	.DB 00H

	.END
